<?php

//require_once __DIR__.'/../vendor/autoload.php';
use GeoIp2\Database\Reader;

if(!defined('APP_INCLUDE')){ die('invalid inclusion'); }

Class DataVisualization {
	var $paginationPanel;	  		# This is a toggle to show the pagination panel under a report or not.
	var $displayHeader;        		# controls if we want to display a report header (default true)
	var $columnDefinitions;
	var $limit;
	var $from;               		# This contains the from timestamp
	var $to;                 		# This contains the to timestamp
	var $labels;             		# This contains the report name
	var $displayReportLabel;   		# controls if we want to print the report name in the header (default false)
	var $allowDateFormat;			# Allows the use of dateFormat from the profile. ( default true )
	var $displayReportButtons; 		# controls if we want to print report buttons in the header for help, print, export, email etc. (default true)
	var $sortTable;
	var $noTotalRow;				
	var $displayTotalRow;			# controls if we want to display a row with totals at the bottom of a table (default true)
	var $HighlightFormatString;		
	var $options = [];
	
	function __construct() {
		global $from, $to, $profile;
		
		$this->from = $from;
		$this->to = $to;
		$this->profile = $profile;
		$this->columnDefinitions = [];
		$this->allowDateFormat = true;
		$this->sortTable = true;
		
		if(empty($this->limit) || !isset($this->limit) ) {
			$this->limit = 10;
		}
		
		if (!isset($this->paginationPanel)) {
			if($this->limit <= 10) {
				$this->paginationPanel = false;
			} else {
				$this->paginationPanel = true;
			}
		}
		
		$this->displayHeader = true;
		$this->displayTotalRow = true;			
	}

	function fieldsArray($getconstant = false) {
		$fields = [];
		foreach($this->columnDefinitions as $definition) {
			if($definition["display"] !== false){
				if(defined($definition["Label"]) && $getconstant === false){
				$fields[] = constant($definition["Label"]);
				} else {
					$fields[] = $definition["Label"];	
				}				
			}
		}
		return $fields;
	}
	
	# this report displays a table containing data
	public function Table($tabledata) {		
		$this->ArrayStatsTable($tabledata,$this->from,$this->to,$this->label);
	}
	
	function DonutGraph($innerRing,$outerRing,$innerLabels,$outerLabels) {
	
		# Create an unique id for the graphcontainer
		$md5_time = md5(time());
		$container_id = 'dg'.$md5_time.rand(1,1000);
		
		echo "<div id='$container_id' style='font-size: 12px;'></div>";
		
		if (!isset($this->innerColors)) {
			$this->innerColors = "'rgba(226,82,29,1)','rgba(119,189,57,1)','rgba(44,181,233,1)','rgba(242,192,43,1)'";
		}
		
		if (!isset($this->outerColors)) {
		# 1 - 10  IE 11 - 20 FF 21 - 30 Other
			$this->outerColors = "'rgb(247,143,106)','rgb(241, 126, 83)'
			,'rgb(241, 156, 125)'
			,'rgb(241, 83, 83)'
			,'rgb(241, 125, 125)'
			,'rgb(226, 82, 29)'
			,'rgb(226, 82, 29)'
			,'rgb(226, 29, 29)'				
			,'rgb(147, 47, 9)'
			,'rgb(147, 9, 9)'
			
			
			,'rgb(179, 222, 137)'
			,'rgb(163, 222, 106)'
			,'rgb(177, 222, 137)'
			,'rgb(130, 211, 135)'
			,'rgb(100, 211, 108)'
			,'rgb(160, 222, 106)'
			,'rgb(50, 166, 58)'
			,'rgb(119, 189, 57)'
			,'rgb(45, 151, 91)'
			,'rgb(68, 123, 19)'
			
			
			,'rgb(131, 238, 243)'
			,'rgb(90, 237, 243)'
			,'rgb(135, 214, 244)'
			,'rgb(139, 193, 245)'
			,'rgb(96, 203, 244)'
			,'rgb(101, 174, 245)'
			,'rgb(44, 181, 233)'
			,'rgb(51, 144, 234)'
			,'rgb(14, 114, 151)'
			,'rgb(16, 86, 152)'
			";
		}
		
		?>
		<script type="text/javascript">
		// Outer Ring
		var s1 = [<?php echo implode(",",$outerRing); ?>];
		
		// Inner Ring
		var s2 = [<?php echo implode(",",$innerRing); ?>];
		
		plot2 = $.jqplot('<?php echo $container_id; ?>', [s1, s2], {
			animate : false,
			grid:{borderWidth:0, shadow:false, background: "rgba(0,0,0,0)"} ,
			series: [{
				seriesColors : [<?php echo $this->outerColors; ?>],
				rendererOptions:{
					dataLabelPositionFactor : 0.9,
					dataLabelNudge : 10,
					 dataLabels: [<?php echo implode(",",$outerLabels); ?>]
				}
			},{
				seriesColors : [<?php echo $this->innerColors; ?>],
				rendererOptions:{
					dataLabels: [<?php echo implode(",",$innerLabels); ?>]
				}
			}],
			seriesDefaults: {
				shadow: false,
				rendererOptions:{
					 sliceMargin: 2,
					 innerDiameter: 50,
					 startAngle: <?php if (isset($this->startangle)) { echo $this->startangle; } else { echo "-100"; } ?>,
					 showDataLabels: true,
					 highlightMouseOver : true
				 },
				renderer:$.jqplot.DonutRenderer
			 },
			 highlighter: {
				showTooltip: true,
				show: true,
				tooltipAxes: 'both',
				yvalues: 2,
				tooltipLocation:'sw',
				formatString: '<?php echo  $this->HighlightFormatString; ?>',
				useAxesFormatters:false,
				showMarker: false
			},
			ticks:[],
			legend: {
				show: false
			},
			cursor:{
				show: false,
				zoom:false,
				showTooltip:false
			}
		});
		plots['<?php echo $container_id; ?>'] = plot2;
		</script>
		<?php
	}
	
	function AutoDonutGraph($data,$groups){
		
		$i = [];
		foreach($groups as $key => $val) {
			$i[] = $key;
		}		
		ksort($data);
		$totals = [];
		$alldata = [];
		$total = 0;
		foreach($data as $b => $entry){
			$totals[$i[$b]] = 0;
		
			foreach($entry as $k => $v){
			
				$totals[$i[$b]] = $totals[$i[$b]] + $v[1];
				$alldata[$v[0]] = $v[1];
				$total = $total + $v[1];
			}
		}
		
		$innerRing = [];
		$outerRing = [];
		foreach($totals as $k => $v){
			$p = round(($v / $total) * 100);
			$innerRing[] = "['$k',$v,'$p%']";
			$innerLabels[] = "'$k $p%'";
		}
		
		foreach($alldata as $k => $v){
			$p = "";
			foreach($groups as $g => $gval) {
				if (in_array($k,$gval)==true) {
					$p = round(($v / $totals[$g]) * 100);
				}
			}
			$outerRing[] = "['$k',$v,'$p%']";
			$outerLabels[] = "'$k $p%'";
			
		}
		
		$this->DonutGraph($innerRing,$outerRing,$innerLabels,$outerLabels);
	}
	
	public function RingChart($data = [], $legendOrientation = "east", $legendDisplay = "block", $datalabels = 'percent',  $actionmenu_type = ''){
		# Return an error when there's no data for this daterange.
		if(!empty($data) && $data == _NO_DATA_FOR_THIS_DATE_RANGE) {
			$this->reportHeader();
			echoNotice(_NO_DATA_FOR_THIS_DATE_RANGE, "margin: 10px;");
			return;
		}
		
		# Return an error when there's no data.
		if(empty($data)) {
			echoNotice(_NO_DATA_TO_DISPLAY." "._CHECK_SETTINGS_FOR_REPORT,"margin:10px;");
			return; 
		}
		
		# Create an unique id for the graphcontainer
		$md5_time = md5(time());
		$container_id = $md5_time.rand(1,1000);	
		
		$datapoints = [];
		foreach($data as $k => $v){
			$datapoints[] = "['{$v[0]}',{$v[1]}]";
			
			if (isset($this->legendlabelstring)) {
				$str = str_replace("%t",$v[0],$this->legendlabelstring);
				$legends[] = str_replace("%v",$v[1],$str);
			} else {
				$legends[] = "'{$v[0]}'";
			}
		}
		
		# Populate the graphcolors array, so always have enough colors.
		$this->graphcolors = $this->PopulateColors($this->graphcolors, ceil(count($legends) / count($this->graphcolors)));
		$this->graphcolors[0] ='#CCCCCC';
		$this->graphcolors[1] ='#E2521D';
		$gc = [];
		foreach($this->graphcolors as $k => $graphcolor) {
			# If this data row is 'hidden'; give it color white, so it's physically invisible.
			if(isset($showcolumns[$k]) && $showcolumns[$k] == 0) {
				$gc[] = "'rgb(255, 255, 255)'";
			} else { # If it's not hidden, just give it the color it should have.
				$gc[] = "'{$graphcolor}'";
			}
		}
		
		$graphcolors_js = implode(",", $gc);
		
		?>
		<script type="text/javascript">
		$(document).ready(function(){
			var dataSet = [<?php echo implode(",",$datapoints); ?>];

			var ringPlot = $.jqplot('<?php echo $container_id; ?>', [dataSet], {
				grid:{borderWidth:0, shadow:false, background: "rgba(0,0,0,0)"} ,
				seriesColors: [<?php echo $graphcolors_js; ?>],
				seriesDefaults: {
					shadow: false,
					renderer:$.jqplot.DonutRenderer,
					rendererOptions:{
						sliceMargin: 0,
						startAngle: -90,
						showDataLabels: true,
						dataLabels: '<?php echo $datalabels; ?>'
					}
				},
				highlighter: {
					show:false
				},
				ticks:[],
				legend: {
					show: false
				},
				cursor:{
					show: false,
					zoom:false,
					showTooltip:false
				}
			});
			plots['<?php echo $container_id; ?>'] = ringPlot;
		});
		</script>
		<?php 
			if($legendOrientation == "east") { 
				$pie_styling = "width: 45%; float: left; "; 
			} else if($legendOrientation == "west") { 
				$pie_styling = "width: 40%; float: right;"; 
			} else if($legendOrientation == "south") { 
				$pie_styling = "width: 100%;"; 
			}
			
			if (isset($this->legendstyle)) {				
				$legendcontainerstyle = $this->legendstyle['container'];
				$legendstyle = $this->legendstyle['legend'];
			} else if ($this->limit > 13) {
				//$graphstyle = "margin: 10px;";
				$legendcontainerstyle = "margin: 0 auto; padding-left: 45px; display:{$legendDisplay};";
				$legendstyle = "width: 45%; float: left;";
			} else {
				//$graphstyle = "width: 60%; float: left; margin: 10px;";
				if($legendOrientation == "south") { 
					$legendcontainerstyle = "width: 100%; float: left; margin: 15px 0px 0px 0px; display:{$legendDisplay};";
				} else {			
					$legendcontainerstyle = "width: 33%; float: left; margin: 15px 0px 0px 0px; display:{$legendDisplay};";
				}
				$legendstyle = "width: 100%; display: block; clear: both;";
			}
		?>
		<div class='graph_area' style=''>
			<div id='<?php echo $container_id; ?>' rel='<?php echo array_sum($datapoints); ?>' style='<?php echo $pie_styling; ?>'></div>
			<?php echo $this->CreateLegend($legends, $legendcontainerstyle, $legendstyle, 28); ?>
			<div class='graph_tooltip'></div>
		</div>
		<?php
		return;
	}
	
	# this report displays a pie chart, for example
	public function PieChart($data = [], $legendOrientation = "east", $legendDisplay = "block", $datalabels = 'percent',  $actionmenu_type = '') {
		# Return an error when there's no data for this daterange.
		if(!empty($data) && $data == _NO_DATA_FOR_THIS_DATE_RANGE) {
			$this->reportHeader();
			echoNotice(_NO_DATA_FOR_THIS_DATE_RANGE, "margin: 10px;");
			return;
		}
		
		# Return an error when there's no data.
		if(empty($data)) {
			echoNotice(_NO_DATA_TO_DISPLAY." "._CHECK_SETTINGS_FOR_REPORT,"margin:10px;");
			return; 
		}
		
		# Return an error when the highest value in a data set is 0.
		if(multimax($data, 1) == 0){
			echoNotice(_NO_DATA_TO_DISPLAY." "._CHECK_SETTINGS_FOR_REPORT,"margin:10px;");
			return;
		}
		
		# Create an unique id for the graphcontainer
		$md5_time = md5(time());
		$container_id = "G" . $md5_time.rand(1,1000);
		
		$datapoints = [];
		$legends = [];
		$matches = [];
		
		$c = 0;
		$dataset = "";
		foreach($data as $key => $val) {
			$datapoints[] = $val[1];
			
			$uparts = explode("##",$val[0]);
			if(isset($uparts[2])) { # it's an IP number
				if(!empty($uparts[2])) {
					$title = $uparts[2];
				} else {
					$title = $uparts[0];
				}
			} elseif(!empty($uparts[1])) { # it's a page with a title
				if (strlen($uparts[1]) > $this->columnDefinitions[0]["displayMaxChars"]) {
					$title = substr($uparts[1],0,$this->columnDefinitions[0]["displayMaxChars"]) . "...";
				} else {
					$title = $uparts[1];
				}
			} else { # it's something different
				if (strlen($uparts[0]) > $this->columnDefinitions[0]["displayMaxChars"]) {
					$title = substr($uparts[0],0,$this->columnDefinitions[0]["displayMaxChars"]) . "...";
				} else {
					$title = $uparts[0];
				}
			}
			if (isset($this->legendlabelstring)) {
				$str = str_replace("%t",$title,$this->legendlabelstring);
				$legends[] = str_replace("%v",$val[1],$str);
			} else {
				$legends[] = "'{$title}'";
			}
			preg_match_all("/<a(.*)>(.*)<\/a>/", $title, $regex_result);
			$matches[] = $regex_result[2];
			
			$c++;
		}
		
		foreach($matches as $match_key => $match) {
			if(!empty($matches[$match_key])) {
				$matches[$match_key] = "'".$match[0]."'";
			} else {
				$matches[$match_key] = $legends[$match_key];
			}
		}
		
		# Fetch the plots that need to be hidden
		$hiddenLegends = $this->GetHiddenLegends($legends);
		
		# Pick the largest of limit and the legend count
		if(count($legends) >= $this->limit) {
			$legendcount = count($legends);
		} else {
			$legendcount = $this->limit;
		}
		
		
		# If we hide this row of data; Set the data for each data point in this row to zero
		foreach($datapoints as $k => $val) {
			if(!empty($hiddenLegends[$this->current_plotting_graph - 1]) && in_array($k, $hiddenLegends[$this->current_plotting_graph - 1])) {
				$datapoints[$k] = 0;
			}
		}
		
		?>
		<script type="text/javascript" charset="utf-8">
		$(document).ready(function() {
			var data = [
				<?php for($c = 0; $c < count($datapoints); $c++) { if($c > 0) { echo " ,"; } ?>[<?php echo $legends[$c]; ?>, <?php echo $datapoints[$c]; ?>]<?php } ?>
			];
			
			var graphlabels = [];
			
			var htmlless_graphlabels = [];
			
			$("#<?php echo $container_id; ?>").bind('jqplotDataMouseOver', function (ev, seriesIndex, pointIndex, data) {
				if(data != null) {
					tooltipcontents = "<strong>" + data[0] + "</strong><br/> " + data[1] + " (" + Math.round((data[1] / $(this).attr('rel')) * 100) + "%)";
					
					$(this).parent().find(".graph_tooltip").css("top", (ev.pageY + 25));
					
					if(ev.pageX - ($(this).parent().find(".graph_tooltip").outerWidth() / 2) < $(this).parent().find(".graph_tooltip").outerWidth() / 2) {
						var tooltip_horizon = ev.pageX;
					} else if(ev.pageX - ($(this).parent().find(".graph_tooltip").outerWidth() / 2) > $("body").outerWidth() - $(this).parent().find(".graph_tooltip").outerWidth()) {
						var tooltip_horizon = ev.pageX - $(this).parent().find(".graph_tooltip").outerWidth();
					} else {
						var tooltip_horizon = ev.pageX - ($(this).parent().find(".graph_tooltip").outerWidth() / 2);
					}
					
					$(this).parent().find(".graph_tooltip").css("left", tooltip_horizon);
					
					$(this).parent().find(".graph_tooltip").html(tooltipcontents);
					
					$(this).parent().find(".graph_tooltip").show();
					$(this).parent().find(".graph_tooltip").css("display","block");
				} else {
					$(this).parent().find(".graph_tooltip").hide();
					$(this).parent().find(".graph_tooltip").css("display","none");
				}
			});
			
			$("#<?php echo $container_id; ?>").bind('jqplotDataUnhighlight', function (ev) {
				$(this).parent().find(".graph_tooltip").hide();
			});
			
			<?php
			# Populate the graphcolors array, so always have enough colors.
			$this->graphcolors = $this->PopulateColors($this->graphcolors, ceil(count($legends) / count($this->graphcolors)));
			
			$gc = [];
			foreach($this->graphcolors as $k => $graphcolor) {
				# If this data row is 'hidden'; give it color white, so it's physically invisible.
				if(isset($showcolumns[$k]) && $showcolumns[$k] == 0) {
					$gc[] = "'rgb(255, 255, 255)'";
				} else { # If it's not hidden, just give it the color it should have.
					$gc[] = "'{$graphcolor}'";
				}
			}
			
			$graphcolors_js = implode(",", $gc);
			?>
			
			plots['<?php echo $container_id; ?>'] = $.jqplot ('<?php echo $container_id; ?>', [data],{
				seriesColors: [<?php echo $graphcolors_js; ?>],
				seriesDefaults: {
					// Make this a pie chart.
					renderer: jQuery.jqplot.PieRenderer,
					rendererOptions: {
						// Put data labels on the pie slices.
						// By default, labels show the percentage of the slice.
						diameter: $("#<?php echo $container_id; ?>").outerHeight(),
						showDataLabels: true,
						dataLabels: '<?php echo $datalabels; ?>',
						dataLabelPositionFactor : 0.8,
						startAngle: -125,
						shadowOffset: 0,
						shadowDepth: 5,
						shadowAlpha: 0.07
					}
				},
				highlighter: {
					show:false
				},
				cursor: {
					show:false
				},
				legend: { show: false },
				grid: {
					shadow: false,
					borderWidth: 0,
					background: 'rgba(0,0,0,0)'
				},
				height: 250,
				gridPadding: {top:0, bottom:0, left:0, right:0}
			});
			
			$.jqplot.eventListenerHooks.push(['jqplotMouseMove', showGraphTooltip]);
			$.jqplot.eventListenerHooks.push(['jqplotMouseLeave', hideGraphTooltip]);
			
			$("#<?php echo $container_id; ?>").closest(".graph_area").append("<div class='graph_tooltip'></div>");
		});
		</script>
		
		<?php 
			if($legendOrientation == "east") { 
				$pie_styling = "width: 45%; float: left; "; 
			} else if($legendOrientation == "west") { 
				$pie_styling = "width: 40%; float: right;"; 
			} else if($legendOrientation == "south") { 
				$pie_styling = "width: 100%;"; 
			}
			
			if (isset($this->legendstyle)) {				
				$legendcontainerstyle = $this->legendstyle['container'];
				$legendstyle = $this->legendstyle['legend'];
			} else if ($this->limit > 13) {
				//$graphstyle = "margin: 10px;";
				$legendcontainerstyle = "margin: 0 auto; padding-left: 45px; display:{$legendDisplay};";
				$legendstyle = "width: 45%; float: left;";
			} else {
				//$graphstyle = "width: 60%; float: left; margin: 10px;";
				if($legendOrientation == "south") { 
					$legendcontainerstyle = "width: 100%; float: left; margin: 15px 0px 0px 0px; display:{$legendDisplay};";
				} else {			
					$legendcontainerstyle = "width: 33%; float: left; margin: 15px 0px 0px 0px; display:{$legendDisplay};";
				}
				$legendstyle = "width: 100%; display: block; clear: both;";
			}
		?>
		<div class='graph_area' style=''>
			<div id='<?php echo $container_id; ?>' rel='<?php echo array_sum($datapoints); ?>' style='<?php echo $pie_styling; ?>'></div>
			<?php echo $this->CreateLegend($legends, $legendcontainerstyle, $legendstyle, 28); ?>
			<div class='graph_tooltip'></div>
		</div>
		<?php
		return;
	}
	
	public function CreateLegend($legends, $containerStyle = "", $legendStyle = "", $maxchars = 27) {
		# Even though Tupac and Bob Marley are legends, they were not made here...
		
		# Get a list of legend items to hide
		$hiddenLegends = $this->GetHiddenLegends($legends);
		
		$legend = "<div class='glegend' style='{$containerStyle}'>";
		for($c = 0; $c < count($legends); $c++) {
			# If the legend items starts or ends with a quote, remove it.
			if(substr($legends[$c], 0, 1) == "'") {
				$legends[$c] = substr($legends[$c], 1);
			}
			if(substr($legends[$c], -1) == "'") {
				$legends[$c] = substr($legends[$c], 0, -1);
			}
			//$legends[$c] = stripslashes($legends[$c]);
			$full_text = strip_tags($legends[$c]);
			$short_text = substr($full_text, 0, $maxchars);
			$short_text_inc_html = str_replace($full_text, $short_text, $legends[$c]);
			
			# Set whether to show or hide a legend item
			if(!empty($hiddenLegends[$this->current_plotting_graph - 1]) && in_array($c, $hiddenLegends[$this->current_plotting_graph - 1])) {
				$legendclass = 'switchable inactive_plot';
			} else {
				$legendclass = 'switchable';
			}
			$legend .= "<div style='color: {$this->graphcolors[$c]}; {$legendStyle}' title='{$full_text}'>";
				if(!empty($this->actionmenu_type)) {
					$actionmenu_link = " style='cursor: pointer;' onclick='popupMenu(event, \"".urlencode($legends[$c])."\", \"{$this->actionmenu_type}\")' ";
				} else {
					$actionmenu_link = "";
				}
				
				$legend .= "<div class='legend_bullet {$legendclass}'>";
					$legend .= "<div style='background-color: {$this->graphcolors[$c]};'></div>";
				$legend .= "</div>";
				
				$legend .= "<label {$actionmenu_link}>".stripslashes($short_text_inc_html);
				if (strlen($short_text) > $maxchars) {
					$legend .= "...";
				}
				$legend .= "</label>";
			$legend .= "</div>";
		}
		
		$legend .= "<div class='clear'></div></div>";
		
		return $legend;
	}
	public function FunnelChart($data = []){
		$md5_time = md5(time()).rand(1,1000);		
		$funnel_labels = [];
		$funnel_data = [];
		
		foreach ($data as $set) {
			$funnel_data[] = $set[2];
			$funnel_labels[] = $set[1];
		}
		
		$jqplot_funnel = "<div class='graph_area' style='width: 250px;'>
			<div class='graph_tooltip'></div>			
			<div id='{$md5_time}' class='graphcontainer' style='height: 250px; margin: 0; width: auto;'>
						
			</div>
			
			<ul class='graphlegend' style='display:none;'>";
			foreach($funnel_labels as $funnel_label) {
				$jqplot_funnel .= "<li><div class='legend_bullet'><div style='background-color: #F00;'></div></div><label>{$funnel_label}</label></li>";
			}
			$jqplot_funnel .= "</ul>
		</div>";
		
		$funnel_labels = json_encode($funnel_labels);
		$funnel_data = json_encode($funnel_data);		
		$plot_options = array(
			"seriesColors" => $this->graphcolors,
			"seriesDefaults" => array(
				"rendererOptions" => array(
					"sectionMargin" => 0,
					"widthRatio" => 0.3,
					"dataLabels" => 'value',
					"highlightMouseOver" => true,
					"shadow" => false,
					"padding" => array(
						"top" => 0,
						"right" => 0,
						"bottom" => 0,
						"left" => 0
					)
				)
			),
			"cursor" => array(
				"show" => false
			),
			"highlighter" => array(
				"show" => false
			),
			"grid" => array(
				"shadow" => false,
				"gridLineColor" => 'rgba(239,239,239,1)',
				"borderWidth" => 0,
				"borderColor" => 'rgba(239,239,239,1)'
			)
		);		
		$plot_options = json_encode($plot_options);		
		$jqplot_funnel .= "<script type='text/javascript'>createFunnel('{$md5_time}', ".$funnel_labels.", ".$funnel_data.", ".$plot_options. ")</script>";
		echo $jqplot_funnel;
	}
	
	public function BubbleChart($data = [], $legendDisplay = "block", $graphstyle = "",$showlabels = 'false') {
		# create a unique container id for our graph
		$md5_time = md5(time());
		$container_id = $md5_time.rand(1, 1000);
		
		# reorder our data array to what the bubble chart wants (x, y, bubblesize, label)
		$data = $this->ReorderColumns($data, $this->bubblefields);
		
		# we need to make a field list with last field at the beginning of the array to get our tool tip right
		$fields = explode(",", $this->bubblefields);
		$last = array_pop($fields);
		array_unshift($fields, $last);
		
		# Set up a legends array.
		$legends = [];
		$datastring = "";
		foreach($data as $row) {
			if (strpos($row[3], '##')!==false) {
				$legends[] = substr($row[3], 0, strpos($row[3], '##'));
			} else {
				$legends[] = $row[3];
			}
		}
		
		# Fetch an array with plots/bubbles to hide
		$hiddenLegends = $this->GetHiddenLegends($legends);
		
		# Change the data array to a string for javascript.
		$datastring = "";
		foreach($data as $k => $row) {
			if (!empty($datastring)) {
				$datastring .= ", ";
			}
			if (strpos($row[3], '##')!==false) {
				$row[3] = '"'.substr($row[3], 0, strpos($row[3], '##')).'"';
			} else {
				$row[3] = '"'.$row[3].'"';
			}
			
			# If we hide this row of data; Set the data for each data point in this row to zero
			if(!empty($hiddenLegends[$this->current_plotting_graph - 1]) && in_array($k, $hiddenLegends[$this->current_plotting_graph - 1])) {
				$row[0] = 0;
				$row[1] = 0;
				$row[2] = 0;
			}
			
			$datastring .= "[".implode(', ',$row)."]";
		}
		
		# Populate the color array so we always have enough colors
		$this->graphcolors = $this->PopulateColors($this->graphcolors, ceil(count($legends) / count($this->graphcolors)));
		
		# create a string of colors for use in javascript 
		$gc = [];
		foreach($this->graphcolors as $k => $graphcolor) {
			# If this data row is 'hidden'; give it color white, so it's physically invisible.
			if(isset($showcolumns[$k]) && $showcolumns[$k] == 0) {
				$gc[] = "'rgb(255, 255, 255)'";
			} else { # If it's not hidden, just give it the color it should have.
				$gc[] = "'{$graphcolor}'";
			}
		}
		$graphcolors_js = implode(",", $gc);
		
		# print the javascript and the html container
		?>
		
		<script type="text/javascript" charset="utf-8">
		$(document).ready(function(){			
			
			<?php
			echo "var arr = [".$datastring."];";
			?>
			plots['<?php echo $container_id; ?>'] = $.jqplot('<?php echo $container_id; ?>',[arr],{
				animate: <?php echo $this->animate_graph ?>,
				animateReplot: <?php echo $this->animate_graph ?>,
				seriesColors: [<?php echo $graphcolors_js; ?>],
				seriesDefaults:{
					renderer: $.jqplot.BubbleRenderer,
					rendererOptions: {
						highlightMouseOver: false,
						bubbleAlpha: 0.95,
						autoscaleMultiplier: 1.2,
						showLabels: <?php echo $showlabels; ?>
					},
					shadow: false,
				},
				cursor:{
					show: true,
					zoom:true,
					showTooltip:false
				},
				highlighter: {
					show:true,
					showTooltip: true,
					showMarker: false,
					tooltipAxes: 'both',
					yvalues: 2,
					tooltipLocation: 'e',
					useAxesFormatters: true,
					formatString: '<table class="jqplot-highlighter" style="color: #333;"><tr><td><?php echo $fields[0]; ?>:</td><td>%s</td></tr><tr><td><?php echo $fields[1]; ?>:</td><td>%s</td></tr><tr><td><?php echo $fields[2]; ?>:</td><td>%s</td><tr><td><?php echo $fields[3]; ?>:</td><td>%s</td></tr></tr></table>'
				},
				grid: {
					background: "rgba(0,0,0,0)",
					shadow: false,
					gridLineColor: 'rgba(239,239,239,1)',
					borderWidth: 1,
					borderColor: 'rgba(239,239,239,1)'
				},
				axes: {
					xaxis: {
						tickRenderer: $.jqplot.CanvasAxisTickRenderer,
						tickOptions: {
							angle: -45,
							fontSize: '9px'
						},
						showLabel: true,
						label:'<?php echo $fields[1];?>',
						labelOptions: {
							fontSize: '10px'
						}
					},
					yaxis: {
						labelRenderer: $.jqplot.CanvasAxisLabelRenderer,
						tickRenderer: $.jqplot.CanvasAxisTickRenderer,
						tickOptions: {
							fontSize: '9px'
							
						},						
						showLabel: true,
						label:'<?php echo $fields[2];?>',
						labelOptions: {
							fontSize: '10px',
							rotate: 90
						}
					}
				}
			});

		});		
		</script>
		<?php
		
		if ($this->limit > 13) {
			if(empty($graphstyle)){
				$graphstyle = "margin:10px;";
			}
			$legendcontainerstyle = "margin:0 auto;padding-left:45px; display:{$legendDisplay};";
			$legendstyle = "width:45%;float:left;";
		} else {
			if(empty($graphstyle)){
				$graphstyle = "width:60%; float:left;margin:10px;";
			}
			$legendcontainerstyle = "width:33%;float:left;margin:15px 0px 0px 0px; display:{$legendDisplay};";
			$legendstyle = "width:100%;display:block;clear:both;";
		}
		
		echo "<div style='overflow:hidden;'>";
		echo "<div class='graph_area' style='$graphstyle'>";
		echo "<div id='{$container_id}'></div>";
		echo "</div>";			
		echo $this->CreateLegend($legends, $legendcontainerstyle, $legendstyle, 22);
		echo "</div>";
	}
	
	function MergeArrays($Arr1, $Arr2) {
	  foreach($Arr2 as $key => $Value)
	  {
		if(array_key_exists($key, $Arr1) && is_array($Value))
		  $Arr1[$key] = $this->MergeArrays($Arr1[$key], $Arr2[$key]);

		else
		  $Arr1[$key] = $Value;

	  }
	  return $Arr1;
	}

	function GetGraphColors($legends){
		# Populate the color array so we always have enough colors.
		$this->graphcolors = $this->PopulateColors($this->graphcolors, ceil(count($legends) / count($this->graphcolors)));
		$gcolors = $this->graphcolors;
		
		foreach($gcolors as $k => $gcolor) {
			if(!empty($hiddenLegends[$this->current_plotting_graph - 1])) {
				foreach($hiddenLegends[$this->current_plotting_graph - 1] as $x) {
					# If the plot is 'hidden', color it white.
					$gcolors[$x] = 'rgb(255, 255, 255)';
				}
			}
		}

		return $gcolors;
	}
	
	public function Graph($data = [], $graph_type = 'line', $actionmenu_type = '', $chartWidth = 0, $chartHeight = 300, $custom_graphoptions = [], $legendOrientation = "south", $legendDisplay = "block") {
		global $reports;

		if(empty($data)) { return false; }
		
		# Create a unique id for our graph
		$md5_time = md5(time()).rand(1,1000);
		
		$graphdata = [];
		$fields = $this->fieldsarray();
		
		# Fix fields array, should any be missing
		$x = 0;
		$newfields = [];
		foreach($fields as $c => $f) {
			$newfields[$x] = $f;
			$x++;
		}
		
		$fields = $newfields;
		$newdata = [];

		foreach($data as $key => $value) {
			$x = 0;
			foreach($value as $k => $v) {
				if($x == 0 || !is_numeric($v)) {
					# The value of the header can not be formated.
					$newdata[$key][$x] = $v;
				} else {
					# This is a normal value, Set the comma values with '.' and the thousands with nothing cause that will f*** up the graph
					$newdata[$key][$x] = number_format($v,2,'.','');
				}
				$x++;
			}
		}
		
		$data = $newdata;
		
		$barcount = 0;
		$linecount = 0;
		$maxNumber = 0;
		$i = 1;
		
		foreach($data as $key => $value) {
			for($c = 0; $c < count($value); $c++) {
				# Find out which graph renderer we should use
				if(!is_array($graph_type)) {
					if($graph_type == 'line') {
						$dataset_type = 'line';
						$linecount++;
					} elseif($graph_type == 'bar') {
						$dataset_type = 'bar';
						$barcount++;
					} elseif($graph_type == 'area') {
						$dataset_type = 'area';
					} elseif($graph_type == 'stackedbar') {
						$dataset_type = 'stackedbar';
					}
				} else {
					if(!empty($graph_type[$c])) {
						if($graph_type[$c] == 'line') {
							$dataset_type = 'line';
							$linecount++;
						} elseif($graph_type[$c] == 'bar') {
							$dataset_type = 'bar';
							$barcount++;
						} elseif($graph_type[$c] == 'area') {
							$dataset_type = 'area';
						} elseif($graph_type[$c] == 'stackedbar') {
							$dataset_type = 'stackedbar';
						}
						
					} else {
						$dataset_type = '';
					}
				}

				if(!isset($fields[$c])){
					continue;
				}
				
				# Set the first entry in each series to include the legend's label, and which plot type it should be
				$graphdata[$c][0] =  "'".addslashes($fields[$c])."|{$dataset_type}"."'";

				if(empty($value[$c])) {
					# If the value does not exists, make it empty.
					$graphdata[$c][$i] = "' '";
				} else {
					if(strpos($graphdata[$c][0], _DATE) != false && $this->allowDateFormat == true) {
						# Convert the date from the file in the date we want in the correct language
						$tempstamp = strtotime($value[$c]);
						$value[$c] = LogaDate($this->profile->dateFormat,$tempstamp);
					}
					
					if(is_numeric($value[$c]) && $value[$c] > $maxNumber) {
						$maxNumber = $value[$c];
					}
					
					$graphdata[$c][$i] = "'".$value[$c]."'";
				}
			}
			$i++;
		}
		$tmp_data = [];
		// this part is mega fishy, check out why tyhe fuck we are doing this
		for($c = 1; $c < count($graphdata); $c++) {
			$total_val = 0;
			for($i = 1; $i < count($graphdata[$c]); $i++) {
				$v = trim(str_replace("'", "", $graphdata[$c][$i]));
				if(!empty($v)) {
					$total_val += $v;
				}
			}
			
			$graphdata[$c][0] = "'".substr($graphdata[$c][0], 1);
			// this is insane, let's change it 4-3-2014
			//$tmp_data[$total_val][] = $graphdata[$c];
			$tmp_data[$c][] = $graphdata[$c];
		}
		
		# this fucks up the data in trend analysis
		// krsort($tmp_data);
		
		# Create the Legends array
		$legends = [];
		foreach($tmp_data as $tmp) {
			foreach($tmp as $temp) {
				$legends[] = substr($temp[0], 0, strpos($temp[0], '|'));
			}
		}
		
		# Get the hidden legend items
		$hiddenLegends = $this->GetHiddenLegends($legends);
		
		$t = [];
		$c = 1;
		foreach($tmp_data as $tmp) {
			foreach($tmp as $temp) {
				if(!empty($hiddenLegends[$this->current_plotting_graph - 1])) {						
					if(in_array(($c - 1),$hiddenLegends[$this->current_plotting_graph - 1])){						
						# Set the data for this data set to zero, if we hide this set
						$t[$c] = array_fill(0, count($temp), "'0'");
						$t[$c][0] = $temp[0];
					} else {
						$t[$c] = $temp;
					}
				} else {
					$t[$c] = $temp;
				}
				
				$c++;
			}
		}
		
		if(!empty($graphdata[0])) {
			$darr = $graphdata[0];
		} else {
			$darr = NULL;
		}
		$graphdata = $t;
		
		$graphdata[0] = $darr;
		
		ksort($graphdata);
		if($maxNumber < 10) {
			$yaxis_format = "%.2f";
		} else {
			$yaxis_format = "%.0f";
		}
		


		#Creates a js array for the colors to use in the graph
		$color_js = $this->GetGraphColors($legends);
		
		
		$graphoptions = array( // default
			"animate" => $this->animate_graph,
			"animateReplot" => $this->animate_graph,
			"seriesColors" => $color_js,
			"seriesDefaults" => array(					
				"rendererOptions" => array(
					"barWidth" => null,
					"barPadding" => 3,
					"barMargin" => 3,
					"animation" => array(
						"speed" => 4000
					),
					"smooth" => true
				),
				"pointLabels" => array(
					"show" => false
				),
				"shadow" => false,
				"lineWidth" => 6,
				"markerRenderer" => "$.jqplot.MarkerRenderer",
				"markerOptions" => array(
					"show" => true,
					"size" => 8,
					"style" => 'circle',
					"color" => "#FFFFFF",
					"lineWidth" => 1,
					"shadow" => false
				),
				"fill" => false,					
				"trendline" => array(
					"color" => '#666666',   // CSS color spec for the trend line.
					"label" => 'Trend',          // label for the trend line.
					"type" => 'linear',     // 'linear', 'exponential' or 'exp'
					"shadow" => true,       // show the trend line shadow.
					"lineWidth" => 1.5,     // width of the trend line.
					"shadowAngle" => 45,    // angle of the shadow.  Clockwise from x axis.
					"shadowOffset" => 1.5,  // offset from the line of the shadow.
					"shadowDepth" => 3,     // Number of strokes to make when drawing shadow.
					// "shadowAlpha" => 0.07,   // Opacity of the shadow	
					"show" => false
				)
			),
			"series" => array(
			),
			"stackSeries" => false,
			"highlighter" => array(
				"show" => false
			),
			"cursor" => array(
				"show" => false,
				"style" => 'default',
				"zoom" => false,
				"showTooltip" => false,
				"showVerticalLine" => true
			),
			"grid" => array(
				"shadow" => false,
				"gridLineColor" => 'rgba(239,239,239,1)',
				"borderWidth" => 1,
				"borderColor" => 'rgba(239,239,239,1)'
			),
			"axes" => array(
				"xaxis" => array(
					"renderer" => "$.jqplot.CategoryAxisRenderer",
					"tickRenderer" => "$.jqplot.CanvasAxisTickRenderer",
					"tickInterval" => 1,
					"tickOptions" => array(
						"angle" => -45,
						"fontSize" => '9px'
					),
					"ticks" => []
				),
				"yaxis" => array(
					"tickRenderer" => "$.jqplot.CanvasAxisTickRenderer",
					"tickOptions" => array(
						"fontSize" => '9px',
						"formatString" => $yaxis_format
					),
					"ticks" => [],
					"min" => 0,
					"showLabel" => true,
					"label" => "",
					"labelOptions" => array(
						"fontSize" => "10px",
						"rotate" => 90
					),
					"rendererOptions" => array(
						"forceTickAt0" => true
					)
				)
			)
		);
		
		if($barcount == 0 && $linecount > 0) { //linechart
						
		} else if($linecount == 0 && $barcount > 0) { //barchart
			
		} else if($barcount > 0 && $linecount > 0) { //barline
			$graphoptions["seriesDefaults"]["lineWidth"] = 3;
		} else if ($dataset_type=='area') {
			$graphoptions["seriesDefaults"]["fill"] = true;
			$graphoptions["stackSeries"] = true;
			
		} else if ($dataset_type=='stackedbar') {
			$graphoptions["stackSeries"] = true;
			
		}
		
		if (!empty($custom_graphoptions)) {
			 $graphoptions = $this->MergeArrays($graphoptions,$custom_graphoptions);
		}

		$new_graphoptions = $graphoptions;
		
		$graphdata_js = "";
		$graphdata_js_array = [];
		$c = 0;
		foreach($graphdata as $value) {
			$graphdata_js_array[$c] = "[";
			if(!empty($value)) {
				$graphdata_js_array[$c] .= implode(",", $value);
			}
			$graphdata_js_array[$c] .= "]";
			$c++;
		}
		
		$graphdata_js = implode(",", $graphdata_js_array);
		?>
		<script type="text/javascript" charset="utf-8">
			var graphdata_<?php echo $md5_time; ?> = [<?php echo $graphdata_js; ?>];
			$(document).ready(function() {
			<?php
			$new_graphoptions = json_encode($new_graphoptions);
			$plotgraph_js = "plotGraph('$md5_time',graphdata_{$md5_time}";
				if(!empty($actionmenu_type)) {
					$plotgraph_js .= ", '{$actionmenu_type}'";
				} else {
					$plotgraph_js .= ", ''";
				}
				$plotgraph_js .= ", {$new_graphoptions}, '{$legendOrientation}_legend', '{$legendDisplay}');";
			echo $plotgraph_js; ?>
			});
		</script>
		<?php
		if($legendOrientation == "east") {
			$chartWidth = "60%; float: left";
		} else if($legendOrientation == "west") {
			$chartWidth = "60%; float: right";
		}
		
		if ($this->limit > 13) {
			$legendcontainerstyle = "margin:0 auto; padding-left: 45px; display:{$legendDisplay};";
			$legendstyle = "width: 45%; float: left;";
		} else {
			if(count($legends) < 10) {
				$legendcontainerstyle = "margin: 10px 0px 0px 0px; display:{$legendDisplay};";
				$legendstyle = "float: left;";
			} else {
				$legendcontainerstyle = "width: 33%; float: left; margin: 15px 0px 0px 0px; display:{$legendDisplay};";
				$legendstyle = "width: 100%; display: block; clear: both;";
			}
		}
		
		$this->actionmenu_type = $actionmenu_type;
		
		$legendoptions = [];
		?>
		<div class='graph_area' style='margin-top: 20px;'>
			<div id='<?php echo $md5_time; ?>' class='graphcontainer' style='<?php if(empty($chartWidth)) { echo "width: auto; "; } else { echo "width: {$chartWidth}; "; } echo "height: {$chartHeight}px;"; ?>'></div>
			<?php echo $this->CreateLegend($legends, $legendcontainerstyle, $legendstyle, 40, $legendoptions); ?>
		</div>
		<?php
		return;
	}
	
	//public function ArrayStatsTable2($data,$from,$to,$labels) {
	public function ArrayStatsTable($data,$from,$to,$labels) {
		global $mini,$nograph,$cachename,$cnames,$gi,$reports,$session;
		
		if (isset($this->gifid) && $this->gifid == 'false') {
			global $profile;
			include "codespace.php";			
			if ($profile->trackermode !=1) {
				echoNotice(_REPORT_NEEDS_GIFDATA." "._TAG_LOGFILES."<br><br>".$codespace2);
			} else {
				echoNotice(_REPORT_NEEDS_GIFDATA."<br><br>".$codespace);
			}
			return;			
		}

		$header = $this->fieldsarray();
		$header_constants = $this->fieldsArray(true); 

		
		$table_id='ReportTable'.md5($labels.$cachename.time().rand(0,1000));
		$this->table_id = $table_id;
		if(is_array($data)) {
            $total_entrys = sizeof($data);
        } else {
            $total_entrys = 0;
        }
		$displayLengthArray = array(10,25,50,100);
		// if ($session->isAdmin()) {
		// 	echo "hallo {$this->limit}, entries ($total_entrys)";
		// }
		$displayLength = 10;
		if(intval($this->limit) <= 100){			
			if($total_entrys < 10){ unset($displayLengthArray[0]); }
			if($total_entrys < 25){ unset($displayLengthArray[1]); }
			if($total_entrys < 50){ unset($displayLengthArray[3]); }
			if($total_entrys < 100){ unset($displayLengthArray[4]); }
			$displayLength = $total_entrys;
		}

		# There is no data but we do need to add a row for no data to show...
		if($displayLength == 0){
			$displayLength = 1;
		}

		if (strpos($reports[$this->labels]['Options'], 'limit') !== false) {
			# if a user can change the limit, let activate the paginator
			$displayLength = 10;
		}		

		# Only change when there is a limit set and we have explicitly set paginationDisplayLength.
		if (!empty($this->paginationDisplayLength) && $this->limit > $this->paginationDisplayLength) { 
			$displayLength = $this->paginationDisplayLength;
		}		
		
		array_push($displayLengthArray,intval($this->limit));
		array_push($displayLengthArray,$total_entrys);
		sort($displayLengthArray);
		$displayLengthArray = array_unique($displayLengthArray);

		$sDom = "t";
		if($this->paginationPanel === true){	$sDom = "tipl"; }
		
		#pick 2 random colors
		$rv = mt_rand(0,count($this->barcolors)-1);
		if ($rv % 2 != 0) {
			$rv--;
		}		
		$color1 = $this->barcolors[$rv];
		$color2 = $this->barcolors[$rv+1];
		
		?>
		<?php
		if(isset($this->label_constant) && isset($reports[$this->label_constant]["Options"])){

			$opts = explode(",",$reports[$this->label_constant]["Options"]);
			if(in_array("columnSelector", $opts)){
				
				$fields = [];
				foreach($header as $k => $v){
					$durp = "showColumn".$k;
					$fields[$durp] = $k;
				}
				if(!empty($fields)) {
					
					$hideCols = array_diff_key($fields, $this->options);
					if(count($fields) != count($hideCols)){
						$hides = implode(",",$hideCols);
					} else {						
						//it's not in the url, check to see if we have default values
						$hides = [];
						foreach($this->columnDefinitions as $k =>$v) {
							if (isset($v['hide_column']) && $v['hide_column']==true) {
								$hides[] = $k;
							}
						}
						$hides = implode(",",$hides);

					}
				}
			} 
		}
		// set up col width based on num cols and see if we have a page col
		$ncols = count($this->columnDefinitions);
		$nwidth = [];
		$hasdouble = '';
		$do_colwidth = false;
		foreach ($this->columnDefinitions as $k => $v) {
			if (isset($v["display"]) && $v["display"]==false) {
				$ncols=$ncols-1;
				continue;
			}
		}
		foreach ($this->columnDefinitions as $k => $v) {
			if (isset($v["display"]) && $v["display"]==false) {				
				continue;
			}

			if ($k==0) {
				$nwidth[] = 40;
			} else {
				$nwidth[] = 60 / ($ncols-1);
			}
			if ($v["Label"] == "_PAGE" || $v["Label"] == "_IP_NUMBER") {
				$hasdouble='hasdouble';
				$do_colwidth = true;
			}
			if ($v["Label"] == "_REFERRER" || $v["Label"] == "_KEYWORDS") {				
				$do_colwidth = true;
			}
		}
		
		?>
		<script language="javascript" type="text/javascript">
		// Define our global variables.
			var conf_name="<?php if (isset($this->profile->profilename)) { echo $this->profile->profilename; } ?>";
			var from_date=<?php if (!empty($from)) { echo $from; } else { echo time(); } ?>;
			var to_date=<?php if (!empty($to)) { echo $to; } else { echo time(); } ?>;
			
			<?php if(!isset($this->skipdatatable)) { ?>
			$(document).ready(function() {
				$('#<?php echo $table_id; ?>').dataTable( {	
				"aaSorting": [],	
				"sDom": '<?php echo $sDom; ?>',
				"bDestroy": true,
				'bAutoWidth': false , 
				"iDisplayLength": <?php echo $displayLength; ?>,
				"language": {
					"info": ui.data.languages['_SHOW'] + " _START_ - _END_ / _TOTAL_",
					"paginate": {
						"previous": ui.data.languages['_PREVIOUS'],
						"next": ui.data.languages['_NEXT']
					},
					"lengthMenu": ui.data.languages['_SHOW']+ " _MENU_ <?php echo _RECORDS; ?>"
				},
				"aoColumnDefs": [
						{ "bVisible": false, "aTargets": [ <?php if(isset($hides)){ echo $hides; }?> ] }
					],
				"aLengthMenu":[<?php echo implode(",",$displayLengthArray); ?>],
				<?php if ($do_colwidth == true) { ?>		 				
						"columns": [
						<?php
							foreach ($nwidth as $w) {
								echo '{ "width": "'.($w).'%" },';
							} 					
						?>
							]

						<?php
					} ?>
				
				});
				gboptions = {};
				$('#<?php echo $table_id; ?>').colResizable({liveDrag:true});
			} );
			<?php } ?>
		</script>

		<table id="<?php echo $table_id; ?>" class="report_table <?php echo $hasdouble; ?>">
			

		<thead>
		<tr class="tabletotalcolor">
		<?php
		# Print the table headers
		if (isset($this->addgraph) && !empty($this->addgraph)) {
				# This must be the first one in the list or it wont work
				echo "<th>"._GRAPH."</th>\n";
		}
		$i=0;
		
		foreach($header as $thisheader) {
			echo "<th>$thisheader</th>\n";
			$i++;
		}
		echo "</tr>\n";
		echo "</thead><tbody>";
		if (isset($this->addgraph) && !empty($this->addgraph)) {
			# this must be the first one in the list or it wont work
			echo "<tr class=><td width='250' rowspan='".(count($data) + 1)."' bgcolor=\"white\" valign=\"top\">{$this->addgraph}</td>\n";          
		}  
		
		# Print the values
		$foravg =1;
		$rn=0;		
		
		$totals = @array_fill(0, $i, 0);
		$maxval = @array_fill(0, $i, 0);
		while (isset($data[$rn])) {
			$ii=0;
			while ($ii < $i) {
				if(!isset($data[$rn][$ii])) { $ii++; continue; }
				if(is_numeric($data[$rn][$ii])) {
					$totals[$ii] = $totals[$ii] + $data[$rn][$ii];
					if ($maxval[$ii] < $data[$rn][$ii]) {
						 $maxval[$ii] = $data[$rn][$ii];
					}
				}
				$ii++;
			}
			$rn++;
		}
		$r = 0;
		$rn = 0;
		if(!isset($this->search)){
			$this->search = "";
		}
		if (!empty($data)) {
			foreach ($data as $thisdatarow) {
				//echo "we have data";
				$ii=0;
				$rn++;
				//alternate bgcolor
				$r++;
				if ($r==2) {
					$gogray="gray_row";
					$gogray="";
					$r=0;
				} else {
					$gogray="";
				}
				// print
				$graphcolor="";
				$rid=md5($labels); // this is used to make each menu id unique for the action menu, so it doesn't mess up when there are multple tables on one page
				
				
				while ($ii < $i) {
					$thisheader = strip_tags($header[$ii]);
					
					# Check date format handling
					if($thisheader == _DATE && $this->allowDateFormat == true){
						# Convert the date from the file in the date we want in the correct language
						$stamp = strtotime($thisdatarow[$ii]);

						# Do we need to skip data because it is not in the range?? (because of month file data)
						if($stamp < $this->from || $stamp > $this->to){
							break;
						}

						$thisdatarow[$ii] = LogaDate($this->profile->dateFormat,$stamp);
					}

					# check if we need to start to echo the row.. why here? well because we could be skipping the row as shown above
					if($ii == 0){
						if (@$addgraph=="") {
							echo "<tr class=' $gogray datarow'>";
							//echo "<tr>";
						} else {
							$addgraph="";
						}		
					}		
							
					# check no handling
					if ($this->columnDefinitions[$ii]["disableShowfieldsHandling"] == true) {
						echo "<td>". $thisdatarow[$ii] ."</td>";
					
					} else if (!empty($this->columnDefinitions[$ii]["actionmenu"])) {						
						# ACTIONMENU START
						$a_type = $this->columnDefinitions[$ii]["actionmenu"];
						$actionData['page'] = [];
						$actionData['keyword'] = [];
						$actionData['referrer'] = [];
						$actionData['params'] = [];
						$actionData['ip'] = [];
						$actionData['country'] = [];

						$actionData['page']['uparts'] = explode("##",$thisdatarow[$ii]);
						$actionData['page']['data'] = $actionData['page']['uparts'][0];
						$actionData['page']['title'] = @$actionData['page']['uparts'][1];
						$actionData['page']['url'] = $actionData['page']['data'];
						
						if (strlen($actionData['page']['url']) > $this->columnDefinitions[$ii]["displayMaxChars"]) { 
							$actionData['page']['url'] = substr($actionData['page']['url'] , 0 , $this->columnDefinitions[$ii]["displayMaxChars"]) . "..."; 
						}					
						if ($actionData['page']['url']=="/") {
						  $actionData['page']['url']="/ (Home Page)";
						}	
						
						$actionData['keyword']['title'] = urldecode($thisdatarow[$ii]);
						$actionData['keyword']['data'] = $thisdatarow[$ii];
						
						$actionData['referrer']['data'] = $thisdatarow[$ii];
						$actionData['country']['data'] = $thisdatarow[$ii];
						$actionData['error']['data'] = $thisdatarow[$ii];
						
						if (strpos($actionData['referrer']['data'], "[G]")!== FALSE) {
						 $gsyn = substr($actionData['referrer']['data'],3);
						 $actionData['referrer']['data'] = $gsyn;
						 $gsyn="<img src=images/google.png border=0 alt='"._VIA_GOOGLE_ADS.": $gsyn'>&nbsp;";
						 $gsyncode="[G]";
						} else {
							$gsyn="";
							$gsyncode="";
						}
						
						$actionData['params']['url'] = (strlen(@$thisdatarow[($ii-1)] . " " . $thisdatarow[$ii]) > 10) ? substr($thisdatarow[$ii],0,7) . "..." : @$thisdatarow[($ii-1)] . " " . $thisdatarow[$ii];
						
						if($a_type == 'page') {
							
							$ptl="";
							if ($actionData['page']['title']!="") {								
							} else {
								$actionData['page']['title'] = ucwords(pathinfo($actionData['page']['url'], PATHINFO_FILENAME));					
							}
							echo "<td class=\"doublecell\"><div class='page-wrap'>";
							echo "<a class=\"\" title=\""._CLICK_TO_OPEN_MENU_FOR." {$actionData['page']['data']}\" onclick=\"popupActionMenu(event, '".urlencode($actionData['page']['data'])."', 'page');\" href=\"\">";

							echo "<span class=\"pagetitle\" title=\"{$actionData['page']['title']}\">";
							
							if (strlen($actionData['page']['title']) > $this->columnDefinitions[$ii]["displayMaxChars"]) {  
								echo substr($actionData['page']['title'],0,$this->columnDefinitions[$ii]["displayMaxChars"]) . "..."; 
							} else { 
								echo $actionData['page']['title']; 
							} 
							echo "</span><br/>";
							
							echo "".urldecode($actionData['page']['url']) ."</a></div>";

							
							
						} else if ($a_type == 'keyword'){					
							echo "<td title='".$actionData['keyword']['title']."'>";					
							echo "<a class='' title='"._CLICK_TO_OPEN_MENU_FOR." {$actionData['keyword']['title']}' onclick=\"popupActionMenu(event, '".urlencode($actionData['keyword']['data'])."', '{$a_type}');\" href=''>";
							
							
							if (strlen($actionData['keyword']['title']) > $this->columnDefinitions[$ii]["displayMaxChars"]) {  
								echo substr($actionData['keyword']['title'],0,$this->columnDefinitions[$ii]["displayMaxChars"]) . "..."; 
							} else { 
								echo $actionData['keyword']['title']; 
							}
							
							echo "</a><br/>";	
							
						} else if ($a_type == 'referrer'){
							$pretty = $actionData['referrer']['data'];
							if (strlen($pretty) > $this->columnDefinitions[$ii]["displayMaxChars"]) {  
								$pretty = substr($pretty,0,$this->columnDefinitions[$ii]["displayMaxChars"]) . "..."; 
							}	
							echo "<td title=\"".$actionData['referrer']['data']."\">";
							echo "<a title=\""._CLICK_TO_OPEN_MENU_FOR." {$actionData['referrer']['data']}\" onclick=\"popupActionMenu(event, '".urlencode($gsyncode.$actionData['referrer']['data'])."', '{$a_type}');\" href=\"\">$gsyn". $pretty ."</a><br/>";				

						} else if ($a_type == 'params') {
							echo "<td title=\"".$thisdatarow[$ii]."\">";
							echo "<a title=\""._CLICK_TO_OPEN_MENU."\" onclick=\"popupActionMenu(event, '".urlencode($thisdatarow[$ii-1]).urlencode($thisdatarow[$ii])."', 'page');\" href=\"\">". $actionData['params']['url'] ."</a><br/>";
		
						} else if ($a_type == 'ip') {
							$ipparts = explode("##",$thisdatarow[$ii]);
							$thisdatarow[$ii] = $ipparts[0];
							$visitorid = @$ipparts[1];

							$visitorlabel = @$ipparts[2];
							echo "<td class=\"doublecell\"><div class='page-wrap'>";
							
							echo "<a onclick=\"popupActionMenu(event, '".$thisdatarow[$ii].";".$visitorid."', 'ipnumber');\" href=\"\">";
							echo "<span class=\"pagetitle\">";
							echo $thisdatarow[$ii];
							echo "</span>";
							if(strlen($visitorid)!=32) { echo "<br><span title=\"$visitorlabel\">user: $visitorid</span>"; 
							} else if(!empty($visitorlabel)){ echo "<br><span>$visitorlabel</span>"; }
							if ($nograph!=1) {
								if(empty($visitorlabel)){
									echo "<br><span id=\"ip$rn\" class='resolve' data-row='{$thisdatarow[$ii]}' data-visitorid='{$visitorid}'><font color=silver>"._RESOLVING."....</font></span>";
								}
							}
							echo "</a></div>";
							$lastknownip=$thisdatarow[$ii];	
						} else if ($a_type == 'country') {
							if ($thisdatarow[$ii]) {
								$cparts=explode(", ", $thisdatarow[$ii]);
								
								$ccode = strtolower(((count($cparts) > 1) && ($cparts[1] > "")) ? $cparts[1] : $cparts[0]);
								$image= "<img hspace=3 width=14  height=11 src=\"images/flags/$ccode.png\" border=0 alt=\"$ccode\">";

								$countryname = $cnames[$cparts[0]];
								$countrynamefull = $countryname;

								if (isset($gi) && @$lastknownip!="") {
									try {
										$area = $gi->city($lastknownip);
									} catch (Exception $e) {
										$area = [];
									}
									if (empty($area)) {
										$countrynamefull=$area->country->name .", " . $area->city->name;

									} else {
										$city = _UNKNOWN_CITY;
										$countrynamefull = '';
									}
									$lastknownip="";
								}

							} else {
								$image = "";
								$countryname = "";
								$countrynamefull = "";
							}
							echo "<td><a onclick=\"popupActionMenu(event, '".urlencode($countryname)."', 'country');\" href=\"\">";
							echo $image." ". $countrynamefull;
							echo "</a><br>";
						} else if ($a_type == 'error') {
							echo "<td><a onclick=\"popupActionMenu(event, '".urlencode($thisdatarow[$ii])."', 'error');\" href=\"\">";
							echo $thisdatarow[$ii];
							echo "</a><br>";
						} else if ($a_type == 'utm_content') {
							echo "<td><a onclick=\"popupActionMenu(event, '".urlencode($thisdatarow[$ii])."', 'utm_content');\" href=\"\">";
							echo $thisdatarow[$ii];
							echo "</a><br>";
						} else if ($a_type == 'utm_source') {
							echo "<td><a onclick=\"popupActionMenu(event, '".urlencode($thisdatarow[$ii])."', 'utm_source');\" href=\"\">";
							echo $thisdatarow[$ii];
							echo "</a><br>";
						} else if ($a_type == 'utm_medium') {
							echo "<td><a onclick=\"popupActionMenu(event, '".urlencode($thisdatarow[$ii])."', 'utm_medium');\" href=\"\">";
							echo $thisdatarow[$ii];
							echo "</a><br>";
						} else if ($a_type == 'utm_campaign') {
							echo "<td><a onclick=\"popupActionMenu(event, '".urlencode($thisdatarow[$ii])."', 'utm_campaign');\" href=\"\">";
							echo $thisdatarow[$ii];
							echo "</a><br>";
						} else if ($a_type == 'utm_term') {
							echo "<td><a onclick=\"popupActionMenu(event, '".urlencode($thisdatarow[$ii])."', 'utm_term');\" href=\"\">";
							echo $thisdatarow[$ii];
							echo "</a><br>";
						}
						echo "</td>";
						# ACTIONMENU END
					} else if ($thisheader==_PATH) {
						$path_parts=explode(" => ",$thisdatarow[$ii]);
						$npp=[];
						foreach ($path_parts as $page) {
							$npp[]= "<span class='pathpart'>$page</span>";
						} 
						$thisdatarow[$ii] = implode(" <img src=images/icons/arrow_right.gif> ",$npp);
						echo "<td>". $thisdatarow[$ii] ."</td>";
						
					} else if ($thisheader==_INTERNAL_KEYWORD) {
						$pretty = urldecode($thisdatarow[$ii]);
						echo "<td title=\"".$pretty."\">";					
						if (strlen($pretty) > $this->columnDefinitions[$ii]["displayMaxChars"]) {  echo substr($pretty,0,$this->columnDefinitions[$ii]["displayMaxChars"]) . "..."; } else { echo $pretty; }
						echo "</td>";
							
						
					} else if ($thisheader==_USER_AGENT) { // CLICKTHROUGH HERE
						echo "<td><a class=' open_in_new_dialog quickopen' name='"._DETAILED_CRAWLER_REPORT."' type='_DETAILED_CRAWLER_REPORT' rel='CrawlerReport' href=\"reports.php?agent=".$thisdatarow[$ii]."&from=$from&to=$to&conf={$this->profile->profilename}&submit=Report&statstable_only=1&labels=_DETAILED_CRAWLER_REPORT\">". $thisdatarow[$ii] ."</a></td>";	
					
					} else if ($thisheader==_SEARCH_RESULT_PAGE) {
						echo "<td title=\"".$thisdatarow[$ii+1]."\">";
						echo $thisdatarow[$ii];// "$page";
						echo "</td>";
						
					} else if ($thisheader==_STATUS) {
							echo "<td><a class=' open_in_new_dialog quickopen' name='".$thisdatarow[$ii]." "._ERROR_REPORT."' type='_DETAILED_ERROR_REPORT' rel='DetailedErrorReport' href=\"reports.php?labels=_DETAILED_ERROR_REPORT&amp;status=".urlencode($thisdatarow[$ii])."&amp;from=$from&amp;to=$to&amp;conf={$this->profile->profilename}&amp;limit={$this->limit}&amp;submit=Report&amp;status=".$thisdatarow[$ii]."\">". $thisdatarow[$ii] ."</a></td>";
					} else if ($thisheader==_COUNTRY && $header_constants[$ii] == "_COUNTRY") {
						if ($thisdatarow[$ii]) {
							$cparts=explode(", ", $thisdatarow[$ii]);
							
							$ccode = strtolower(((count($cparts) > 1) && ($cparts[1] > "")) ? $cparts[1] : $cparts[0]);
							$image= "<img hspace=3 width=14  height=11 src=\"images/flags/$ccode.png\" border=0 alt=\"$ccode\">";

							$countryname = $cnames[$cparts[0]];
							if (isset($gi) && @$lastknownip!="") {
								try {
									$area = $gi->city($lastknownip);//Us Last
								} catch (Exception $e) {
									$area = [];
								}
								if (!empty($area)) {
									$countrynamefull=$area->country->name .", " . $area->city->name;
								} else {
									$city = _UNKNOWN_CITY;
									$countrynamefull = '';
								}
								$lastknownip="";
							}
						} else {
							$image = "";
							$countryname = "";
						}
						echo "<td>". $image ." <a class=' open_in_new_dialog quickopen' name='"._TOP_CITIES."' type='_TOP_CITIES' rel='TopCities' href=\"reports.php?conf={$this->profile->profilename}&amp;from=$from&amp;to=$to&amp;submit=Report&amp;labels=_TOP_CITIES&amp;statstable_only=1&amp;country=". $thisdatarow[$ii] ."\">". $countryname ."</a></td>";

						
					} else if ($this->columnDefinitions[$ii]["bchart"] == true && ($nograph!=1)) {
						@$val=$thisdatarow[$ii];
						if ($totals[$ii]>0){
							$perc = ($val/$totals[$ii])*100;
							$oriperc=$perc;	
							
							$perc=intval($perc);

							$percwidth = ($val/$maxval[$ii])*100;
						} else {
							$perc=0;
							$oriperc=0;
							$width=0;
							$percwidth=0;
						}

						if ($graphcolor==$color1) {
							$graphcolor=$color2;
						} else {
							$graphcolor=$color1;
						}

						if ($labels==_DISABLE_FUNNEL_ANALYSIS) {
							//inflate low numbers
							if ($width < 20) {
								$imageleft ="";
								$imageright="";  
							} else {
								$imageleft ="<img src=images/funnelleft.gif>";
								$imageright="<img src=images/funnelright.gif>";   
							}
							
							echo "<td align=center><table cellpadding=0 cellspacing=0 border=0 class= style=\"margin:0px;line-height:20px;\"><tr><td align=left valign=top>$imageleft</td><td width=\"$width\" class=graphborder_funnel align=center title=\"". number_format($oriperc,1) ." %\">&nbsp;".number_format($val)."&nbsp;";
							
							echo "</td><td align=right valign=top>$imageright</td></tr></table>";
						} else {
							$ori_val = $val;
							if(is_numeric($val)) {
								if ($this->columnDefinitions[$ii]["decimals"]!==false) {
									$val = number_format($val,$this->columnDefinitions[$ii]["decimals"]);
								} else if ($this->columnDefinitions[$ii]["seconds"]!==false) {
									$val = $this->sec2time($val);
								} else {			
									$val = number_format($val);
								}
							} else {
								$val = $val;
							}
							//echo "<td style='white-space:nowrap;'><div sort=\"{$ori_val}\" class='graphborder' style='width:{$width}px;min-width:".(strlen($val)*7)."px;background-color:{$graphcolor};' title='".number_format($oriperc,1)." %'><span class='graphborder' style='background-color:{$graphcolor};border-right:0;'>".$val."</span></div>";
							//echo "<td style='white-space:nowrap;'><div sort=\"{$ori_val}\" class='graphborder' style='width:{$width}px;min-width:".(strlen($val)*10)."px;background-color:{$graphcolor};' title='".number_format($oriperc,1)." %'>".$val."</div>";
							echo "<td style='white-space:nowrap;' class='gr'><div sort=\"{$ori_val}\" class='graphborder' style='width:{$percwidth}%;background-color:{$graphcolor};' title='".number_format($oriperc,1)." %'>".$val;
							if ($this->columnDefinitions[$ii]["percentage"] == true) { echo "%"; }
							echo "</div>";
						}
					} else {
						if (($thisheader==_PAGEVIEWS || $thisheader==_REQUESTS || $thisheader==_HITS || $thisheader==_TOTAL_HITS || $thisheader==_CRAWLED_PAGES  || $thisheader==_UNIQUE_IPS || $thisheader==_UNIQUE_IDS  || $thisheader==_BOT_REQUESTS || $thisheader==_VISITORS || $thisheader==_TOTAL_REQUESTS || $thisheader==_TOTAL_PAGES || $thisheader==_VIEWED_PAGES || $thisheader==_USERS ||$thisheader==_RECORDS ||$thisheader==_SUM_DAILY_UNIQUE_VISITORS)) {
						   $thisdatarow[$ii]=number_format($thisdatarow[$ii],0);
						} else if ($thisheader==_CONVERSION || $thisheader==_BOUNCE_RATE || $thisheader==_RETENTION_PERC || $thisheader==_VISIT_SHARE || $this->columnDefinitions[$ii]["percentage"] == true) {
							 $thisdatarow[$ii]=number_format($thisdatarow[$ii],2) . "%";
						}else if(!isset($thisdatarow[$ii])){
							$thisdatarow[$ii] = '';					
						}else if (is_int($thisdatarow[$ii])==TRUE) {
							 $thisdatarow[$ii]=number_format($thisdatarow[$ii],0);
						} else if (is_numeric($thisdatarow[$ii]) == TRUE && $this->columnDefinitions[$ii]["dataType"]!=="String") {
							 $thisdatarow[$ii] = $thisdatarow[$ii] * 1; // turn it into a real number, not a string
							 if (is_int($thisdatarow[$ii])== TRUE) {
								$thisdatarow[$ii]=number_format($thisdatarow[$ii],0);    
							 } else {
								$thisdatarow[$ii]=number_format($thisdatarow[$ii],2);
							 }					 
						}					
						if ($thisdatarow[$ii] < 0) {
							echo "<td><font color=red>". $thisdatarow[$ii] ."</font></td>";
						} else {
							echo "<td>". $thisdatarow[$ii] ."</td>";   
						}
					}
					$ii++;
				}

				if($ii > 0){
					echo "</tr>\n";
				}
			}
		} else{
			echo "<tr>";
			$ii = 0;
			while ($ii < $i) {
				if($ii === 0){ 
					echo "<td class=''>"._NO_DATA_IN_DATE_RANGE."</td>";
				} else {
					echo "<td></td>";  
				}
				$ii ++;
			} 
			echo "</tr>";
		}
		echo "</tbody>";
		$ii=0;
		
		# Print the Totals
		if ($this->displayTotalRow == true) {
			echo "<tfoot><tr class=\"tabletotalcolor\">";
			while ($ii < $i) {
				if (trim($header[$ii])==_CONVERSION_RATE) {
						$totals[$ii]=0;
				}
				
				if ($totals[$ii] < 10) { $ftype="2"; } else { $ftype="0"; }
				
				echo "<td><b>";
				if(!empty($this->columnDefinitions[$ii]["totalRow"]) && $totals[$ii] != 0){
					$totalType = $this->columnDefinitions[$ii]["totalRow"];
					if($totalType == "none"){
						# No total needed.. Do nothing
					} else if($totalType == "avg"){
						$a = ColumnArray($data,$ii);
						echo number_format((array_sum($a)/count($a)),2); 
					} else if($totalType == "weighted-avg"){
						$wa = $this->WeightedAverage($data,$ii,$this->columnDefinitions[$ii]["weight-row"]);
						if(!empty($wa)){
							echo $wa; 
						} else {
							echo 0;
						}

					} else if($totalType == "sum"){
						echo number_format($totals[$ii], $ftype);
					}					
				} else if ($totals[$ii] != 0) {
					# This list says which columns not to produce a total for
					 if ($header[$ii]==_VISITS_PER_USER || $header[$ii]==_PAGES_PER_USER) {
						$a = ColumnArray($data,$ii);
						echo number_format((array_sum($a)/count($a)),2);    
					} else if (in_array($header[$ii],$this->noTotalRow) == false) {
						echo number_format($totals[$ii], $ftype);
					}
				} else {
					echo "<p class='pdfTotalRow'>Showing 1 - {$displayLength} Total of {$total_entrys} row(s)</p>";
				}
				echo "</b></td>";
				$ii++;
			}
		 echo "</tr></tfoot>\n";
		}
		echo "</table>";
		echo "<div style='clear:both;'></div>";
		# Reset nograph
		$nograph=0;
	}
	

	function CSVStatsTable($data,$from,$to,$labels) {
		global $db;
		
		$filename = $this->conf."-".str_replace(" ","-", trim($labels) ).".csv";
		
		ob_start();
		header("Window-target: _blank");
		header("Content-type: application/x-download");
		header("Content-Disposition: attachment; filename={$filename}");
		header("Content-Transfer-Encoding: binary");
		
		$nicefrom = LogaDate($this->profile->dateFormat,$this->from);
		$niceto = LogaDate($this->profile->dateFormat,$this->to);
		$header = $this->fieldsarray();
	  
		echo "\"".str_replace("%20", " ", trim($labels) ); 
		echo " ".strip_tags(printSegmentName()); 
		echo " "._DATE_FROM." {$nicefrom} {$niceto}\"\r\n";
		//Print the table headers
		$i=0;
		foreach($header as $thisheader) {
			echo "\"{$thisheader}\",";
			$i++;
		}
		echo "\r\n";
		
		//Print the data 
		$r = 0;
		foreach ($data as $thisdatarow) {
			//echo "we have data";
			$ii=0;
			
			while ($ii < $i) {
				if($header[$ii]==_DATE && $this->allowDateFormat === true) {
					# Convert the date from the file in the date we want in the correct language
					$tempstamp = strtotime($thisdatarow[$ii]);
					$thisdatarow[$ii] = LogaDate($this->profile->dateFormat,$tempstamp);
				}

				// if (($labels == _MOST_ACTIVE_USERS || $labels == _RECENT_VISITORS) && $header[$ii] == _IP_NUMBER) {					
				// 	$parts = explode("##",$thisdatarow[$ii]);
				// 	$thisdatarow[$ii] = $parts[0];
				// }
				if ($header[$ii] == _PAGE || $header[$ii] == _LANDING_PAGE) {
					$uparts = explode("##",$thisdatarow[$ii]);
					$thisdatarow[$ii] = $uparts[0];        
				}
				echo "\"". $thisdatarow[$ii] ."\",";
				$ii++;
			}
			echo "\r\n";
		}
		ob_end_flush();
	}
	
	function XMLStatsTable($data,$from,$to,$labels) {
		
		$header = $this->fieldsarray();
		header("Content-type: text/xml");
		iconv_set_encoding('output_encoding', 'UTF-8');
		echo('<?xml version="1.0" encoding="UTF-8"?>');
		
		$i= count($header);		
		
		
		echo "\n<dataset id=\"".$labels."\" >\n";
		if (strtolower($labels)==strtolower(_TOP_CONTINENTS) || strtolower($labels)==strtolower(_TOP_COUNTRIES_CITIES) || strtolower($labels)==strtolower(/*"Top Cities Map")*/_TOP_CITIES)) {
			foreach ($data as $thisdatarow) {
				//echo "we have data";
				$ii=0;
				echo "\t<entry ";
				while ($ii < $i) {
					//echo strtolower(str_replace(" ","_",$header[$ii]))."='".$thisdatarow[$ii]."' ".strtolower(str_replace(" ","_",$header[$ii]))."Prefix"."='' ".strtolower(str_replace(" ","_",$header[$ii]))."Suffix"."='' ".strtolower(str_replace(" ","_",$header[$ii]))."Color='009900' ";
					
					if(strtolower($header[$ii]) == strtolower(_DATE) && $this->allowDateFormat === true) {
						# Convert the date from the file in the date we want in the correct language
						$tempstamp = strtotime($thisdatarow[$ii]);
						$thisdatarow[$ii] = LogaDate($this->profile->dateFormat,$tempstamp);
					}

					if(strtolower($header[$ii]) != strtolower(_COUNTRIES)
						&& strtolower($header[$ii]) != strtolower(_CITY)
						&& strtolower($header[$ii]) != strtolower(_CONTINENT)
						&& strtolower($header[$ii]) != strtolower('longitude')
						&& strtolower($header[$ii]) != strtolower('latitude')
					) {
						echo "value".$ii."Name=\"".$header[$ii]."\" ";
						echo "value".$ii."Value='".$thisdatarow[$ii]."' ";
						echo "value".$ii."Prefix='' ";
						echo "value".$ii."Suffix='' ";
						echo "value".$ii."Color='D63C06' ";
					}
					if(strtolower($header[$ii]) == strtolower(_COUNTRIES)) {
						echo "country='".$thisdatarow[$ii]."' ";
						echo "countryPrefix='' ";
						echo "countrySuffix='' ";
					}
					if(strtolower($header[$ii]) == strtolower(_CITY)) {
						//echo "city='<![CDATA[".$thisdatarow[$ii]."]]>' ";
						echo "city=\"".$thisdatarow[$ii]."\" ";
						echo "cityPrefix='' ";
						echo "citySuffix='' ";
					}
					if(strtolower($header[$ii]) == strtolower(_CONTINENT)) {
						echo "continent=\"".$thisdatarow[$ii]."\" ";
						echo "continentPrefix='' ";
						echo "continentSuffix='' ";
					}
					if(strtolower($header[$ii]) == strtolower('longitude')) {
						echo "longitude='".$thisdatarow[$ii]."' ";
					}
					if(strtolower($header[$ii]) == strtolower('latitude')) {
						echo "latitude='".$thisdatarow[$ii]."' ";
					}
					$ii++;
					if($ii > 3) { break; }
				}
				echo " />\n";
			}
		} else {
			foreach ($data as $thisdatarow) {
				$ii=0;
				echo "\t<entry ";
				while ($ii < $i) {

					if($header[$ii]==_DATE && $this->allowDateFormat === true) {
						# Convert the date from the file in the date we want in the correct language
						$tempstamp = strtotime($thisdatarow[$ii]);
						$thisdatarow[$ii] = LogaDate($this->profile->dateFormat,$tempstamp);
					}

					// if (($labels==_MOST_ACTIVE_USERS || $labels==_RECENT_VISITORS) && $header[$ii]==_IP_NUMBER) {
					// 	$parts = explode("##",$thisdatarow[$ii]);
					// 	$thisdatarow[$ii] = $parts[0];
					// }
					if ($header[$ii]==_PAGE || $header[$ii]==_LANDING_PAGE) {
						$uparts = explode("##",$thisdatarow[$ii]);
						$thisdatarow[$ii] = $uparts[0];        
					}
					echo strtolower(str_replace(" ","_",$header[$ii]))."='".safeXML($thisdatarow[$ii])."' ";
					$ii++;
				}
				echo " />\n";
			}
		}
		echo "</dataset>";
	}

	function ListHiddenColumns($headers){
		global $reports;
		$hidden = [];
		if(isset($this->label_constant) && isset($reports[$this->label_constant]["Options"])){
			$opts = explode(",",$reports[$this->label_constant]["Options"]);
			if(in_array("columnSelector", $opts)){
				$fields = [];
				foreach($headers as $k => $v){
					$durp = "showColumn".$k;
					$fields[$durp] = $k;
				}
				if(!empty($fields)) {
					$hideCols = array_diff_key($fields, $this->options);
					if(count($fields) != count($hideCols)){
						$hidden = implode(",",$hideCols);
					}
				}
			}
		}
		return $hidden;
	}


	function SimpleStatsTable($data,$from,$to,$labels) {
		global $db;

		# Fall back to set an empty array even if the data is a boolean false
		if(empty($data)){
			$data = [];
		}

		$nicefrom = LogaDate($this->profile->dateFormat,$this->from);
		$niceto = LogaDate($this->profile->dateFormat,$this->to);

		$table_id = md5('ReportTable'.$labels.time(). rand(0,1000));

		$header = $this->fieldsarray();
		echo "<table id=\"$table_id\" class=\"report_table\" cellspacing='0'>";
		
		echo "<thead><tr>\n";
		//Print the table headers
		$i=0;
		foreach($header as $thisheader) {
			echo "<th>$thisheader</th>";
			$i++;
		}
		echo "</tr></thead>\r\n";
				
		# Print the data
		$r = 0;
		$rn = 0;
		$totals = [];
		
		while (isset($data[$rn])) {
			
			$ii=0;
			while ($ii < $i) {
				if(isset($totals[$ii])){
					$totals[$ii] = @intval($totals[$ii] + $data[$rn][$ii]);
				}else{
					$totals[$ii] = @intval($data[$rn][$ii]);
				}
				$ii++;
			}
			$rn++;
		}
		echo "<tbody>";
		foreach ($data as $thisdatarow) {
			echo "<tr>";
			$ii=0;
			while ($ii < $i) {
				$pbar = "";	
				if (($labels==_MOST_ACTIVE_USERS || $labels==_RECENT_VISITORS) && $header[$ii]==_IP_NUMBER) {
					$rowparts = explode("##",$thisdatarow[$ii]);
					
					$thisdatarow[$ii]=$rowparts[0];
					if (strlen($rowparts[1])!=32) {						
						$thisdatarow[$ii].=" - ".$rowparts[1];
					}
					if (!empty($rowparts[2])) {						
						$thisdatarow[$ii].=" - ".$rowparts[2];
					}
				}
				if (is_numeric($thisdatarow[$ii])) { 
					if (strpos($thisdatarow[$ii],".")!==false) {
						$thisdatarow[$ii] = number_format($thisdatarow[$ii],2);
					} else {
						$thisdatarow[$ii] = number_format($thisdatarow[$ii]);
					}
				}
				if ($header[$ii]==_VISIT_SHARE) {
					$thisdatarow[$ii] = number_format($thisdatarow[$ii],2) . "%";        
				}
				if ($header[$ii]==_PAGE || $header[$ii]==_LANDING_PAGE) {
					$uparts = explode("##",$thisdatarow[$ii]);
					$thisdatarow[$ii] = $uparts[0];
					//$pbar = "<div class=\"progress xs\"><div class=\"progress-bar progress-bar-aqua\" role=\"progressbar\" aria-valuenow=\"20\" aria-valuemin=\"0\" aria-valuemax=\"100\" style=\"width: 20%\"><span class=\"sr-only\">20% Complete</span></div></div>";
				}


				if($header[$ii] == _DATE && $this->allowDateFormat === true) {
					# Convert the date from the file in the date we want in the correct language
					$tempstamp = strtotime($thisdatarow[$ii]);
					$thisdatarow[$ii] = LogaDate($this->profile->dateFormat,$tempstamp);
				}

				echo "<td>". $thisdatarow[$ii] ."$pbar</td>";
				$ii++;
			}
			echo "</tr>\r\n";
		}

		echo "</tbody>";
		$ii=0;
		
		# Print the Totals
		if ($this->displayTotalRow === true) {
			echo "<tfoot><tr class=\"tabletotalcolor\">";
			if(!empty($data)){
				while ($ii < $i) {				
					if ($totals[$ii] < 10) { $ftype="2"; } else { $ftype="0"; }
					
					echo "<td><b>";
					if(!empty($this->columnDefinitions[$ii]["totalRow"])){
						$totalType = $this->columnDefinitions[$ii]["totalRow"];
						if($totalType == "none"){
							# No total needed.. Do nothing
						} else if($totalType == "avg"){
							$a = ColumnArray($data,$ii);
							if(!empty($a)){
								echo number_format((array_sum($a)/count($a)),2); 
							} else {
								echo 0;
							}

						} else if($totalType == "weighted-avg"){
							$wa = $this->WeightedAverage($data,$ii,$this->columnDefinitions[$ii]["weight-row"]);
							if(!empty($wa)){
								echo $wa; 
							} else {
								echo 0;
							}

						} else if($totalType == "sum"){
							echo number_format($totals[$ii], $ftype);
						}					
					} else if ($totals[$ii] != 0) {
						# This list says which columns not to produce a total for
						 if ($header[$ii]==_VISITS_PER_USER || $header[$ii]==_PAGES_PER_USER) {
							$a = ColumnArray($data,$ii);
							if(!empty($a)){
								echo number_format((array_sum($a)/count($a)),2); 
							} else {
								echo 0;
							}						   
						} else if (in_array($header[$ii],$this->noTotalRow) == false) {
							echo number_format($totals[$ii], $ftype);
						}
					}
					echo "</b></td>";
					$ii++;
				}
			}
		 echo "</tr></tfoot>\n";
		}
		echo "</table>\n"; 

		$hidden = $this->ListHiddenColumns($header);
		?><script type="text/javascript">
		$("#<?php echo $table_id; ?>").dataTable({
			"bPaginate": <?php echo ($rn > 100) ? 'true' : 'false';  ?>,
			 "bLengthChange": <?php echo ($rn > 100) ? 'true' : 'false';  ?>,
			 "bFilter": <?php echo ($rn > 100) ? 'true' : 'false';  ?>,
			 "bSort": true,
			 "bInfo": true,
			 "bAutoWidth": true,
			"aoColumnDefs": [
				{ "bVisible": false, "aTargets": [ <?php if(!empty($hidden)){ echo $hidden; }?> ] }
			]
		});
		</script><?php
	}
	
	function WeightedAverage($data,$ac,$wc) {
		$tot = 0;
		$wtot = 0;

		foreach ($data as $r) {
			//dump($wc);
			//cho "$tot + ({$r[$ac]} * {$r[$wc]})";
			$tot = $tot + ($r[$ac] * $r[$wc]);
			$wtot = $wtot + $r[$wc];
		}
		if ($wtot > 0) {
			$wa = $tot / $wtot;
		} else {
			$wa = 0;
		}
		return number_format($wa, 2);
	}

	
	# reset the columns 
	function ResetColumnNames($keys = []){
		$this->columnDefinitions = [];
		foreach($keys as $key){
			$this->columnDefinitions[] = array("Label" => $key);
		}
		$this->setupColumnDefinitions();
	}
	
	#Default setup columnDefinitions
	function setupColumnDefinitions() {		
		#Array with columns that are bcharts by default.
		$defBchart = array("_PAGEVIEWS","_REQUESTS","_HITS","_CRAWLED_PAGES","_UNIQUE_IPS","_UNIQUE_IDS","_VISITORS","_UNIQUE_VISITORS","_TOTAL_REQUESTS","_TOTAL_PAGES","_VIEWED_PAGES","_VISITS","_BOTS","_EXITS","_SIZE_IN_MB","_RECORDS","_SALES ","_UNITS","_REVENUE","_AVERAGE_REVENUE_P_SALE","_RESPONSES","Friends","_SUM_DAILY_UNIQUE_VISITORS");
		
		foreach($defBchart as $redefine){
			if(defined($redefine)){
				array_push($defBchart,constant($redefine));
			}
		}

		$i = 0;
		foreach($this->columnDefinitions as $definition){
			
			# Check if settings are set. else set the settings to default.

			if(!isset($definition["totalRow"])) {
				$this->columnDefinitions[$i]["totalRow"] = "";
			}
			
			if(!isset($definition["search"])) {
				$this->columnDefinitions[$i]["search"] = false;
			}
			
			
			#"dataType" => "auto" // set default on auto (do what it always tries) or set to Numeric or String
			if(!isset($definition["dataType"])) {
				$this->columnDefinitions[$i]["dataType"] = "auto";
			}
			
			#"disableShowfieldsHandling" => false
			if(!isset($definition["disableShowfieldsHandling"])) {
				$this->columnDefinitions[$i]["disableShowfieldsHandling"] = false;
			}
			
			#"bchart" => false
			if(!isset($definition["bchart"])) {
				if(in_array($definition["Label"],$defBchart)) {
					$this->columnDefinitions[$i]["bchart"] = true;
				} else {
					$this->columnDefinitions[$i]["bchart"] = false;
				}
			}
			#"percentage" => false
			if(!isset($definition["percentage"])) {
				$this->columnDefinitions[$i]["percentage"] = false;
			}
			#"decimals" => false
			if(!isset($definition["decimals"])) {
				$this->columnDefinitions[$i]["decimals"] = false;
			}
			#"seconds" => false
			if(!isset($definition["seconds"])) {
				$this->columnDefinitions[$i]["seconds"] = false;
			}
			
			#"display" => true			
			if(!isset($definition["display"]) || !is_bool($definition["display"])) {
				$this->columnDefinitions[$i]["display"] = true;
			}
			
			#"urldecode" => true
			if(!isset($definition["urldecode"])) {
				$this->columnDefinitions[$i]["urldecode"] = true;
			}
			
			#"actionmenu" => ""
			if(!isset($definition["actionmenu"])) {
				$this->columnDefinitions[$i]["actionmenu"] = "";
			}
			
			#"displayMaxChars" => 100
			if(!isset($definition["displayMaxChars"])) {
				$this->columnDefinitions[$i]["displayMaxChars"] = 1000;
			}
			
			
			# Next Entry..
			$i ++;
		}

		$hidden = $this->ListHiddenColumns( $this->fieldsarray() );
		if(!empty($hidden)){
			$keys = explode(",", $hidden);
			foreach ($keys as $k) {
				$this->columnDefinitions[$k]["disable_line"] = "true";
				$this->columnDefinitions[$k]["show_line"] = false;
			}
		}

	}
	
	
	function WordCloud($data) {
		# beta function, not used yet, require d3 js library
		# create a unique container id for our graph
		$md5_time = md5(time());
		$container_id = $md5_time.rand(1, 1000);
		$container_id = "wordcloud".rand(1, 1000);
	
		echo "<div id=\"{$container_id}\" style='width:500px;height:500px;'></div>";
		?>
		
		<script>

		var fill = d3.scale.category20();
  
		  var mwords = [];
		  
		  <?php
		  
		  $i = 1;
		  foreach ($data as $key => $row) {
				if ($row[0]=="(not provided)") {
					//$row[0] = "notprovided";
				}
				$size = intval(70 / $i);
				if ($size < 10) {
					$size = 10;
				}
				echo "mwords[$key] = ['{$row[0]}', $size];";
				$i++;
		  }
		  ?>
		  // mwords[0] = ['het', 40];
		  // mwords[1] = ['is', 30];
		  // mwords[2] = ['logaholic', 10];
		  // mwords[3] = ['gelukt', 50];
		  // mwords[4] = ['om een', 5];
		  // mwords[5] = ['web analytics', 18];
		  // mwords[6] = ['ding', 12];

		  //console.log(mwords); 
		 
		  
		  d3.layout.cloud().size([500, 500])
			  .words([0,1,2,3,4,5,6,7,8,9].map(function(d) {
				return {text: mwords[d][0], size: mwords[d][1]};
			  }))
			  .rotate(function() { return 0})
			  .font("Impact")
			  .fontSize(function(d) { return d.size; })
			  .on("end", draw)
			  .start();

		  function draw(words) {
			d3.select("#<?php echo $container_id;?>").append("svg")
				.attr("width", 500)
				.attr("height", 500)
			  .append("g")
				.attr("transform", "translate(250,250)")
			  .selectAll("text")
				.data(words)
			  .enter().append("text")
				.style("font-size", function(d) { return d.size + "px"; })
				.style("font-family", "Impact")
				.style("fill", function(d, i) { return fill(i); })
				.attr("text-anchor", "middle")
				.attr("transform", function(d) {
				  return "translate(" + [d.x, d.y] + ")rotate(" + d.rotate + ")";
				})
				.text(function(d) { return d.text; });
		  }
		</script>		
		<?php

	}

	function WorldMap($data,$customheight = 400,$mapbgColor = "#505050"){
		global $cnames, $reports;
		
		# Same as old map 
		if (isset($this->outputmode)) {
			$link = "&outputmode=".$this->outputmode;
		} else {
			$link="";
		}
		
		if (!isset($this->ColorScaleStart)) {
			$this->ColorScaleStart = "#87D6F4";
		}
		if (!isset($this->ColorScaleEnd)) {
			$this->ColorScaleEnd = "#0078AE";
		}
		
		$countrys = [];
		
		$jmapdata[0] = "";
		$jmapdata[1] = "";
		$jmapdata[2] = "";
		$i = 0;
		foreach($data as $key => $row) {
			$countryname = @$cnames[$row[0]];
			$ccode = strtolower($row[0]);
			$image= "<img hspace=3 width=14  height=11 src=\"images/flags/{$ccode}.png\" border=0 alt=\"{$ccode}\">";
			
			if($i == 0){
			$jmapdata[0] .= '"'. strtoupper($ccode) .'": '.$row[1];
			$jmapdata[1] .= '"'. strtoupper($ccode) .'": '.$row[2];
			$jmapdata[2] .= '"'. strtoupper($ccode) .'": "'.$row[3].'"';
				$i ++;
			} else {
				$jmapdata[0] .= ',"'. strtoupper($ccode) .'": '.$row[1];
				$jmapdata[1] .= ',"'. strtoupper($ccode) .'": '.$row[2];
				$jmapdata[2] .= ',"'. strtoupper($ccode) .'": "'. $row[3].'"';
			}

			# without @ you get error: Creating default object from empty value, if empty do statement does not fix this.
			@$tmp_code->country = $ccode;
			
			$data[$key][0] = $image.'<a class=\'open_in_new_dialog quickopen\' options=\''.json_encode($tmp_code).'\' href=\'reports.php?conf='.$this->profile->profilename.'&labels=_TOP_CITIES&statstable_only=1&country='.$row[0].$link.'\' rel=\'TopCities\' type=\'_TOP_CITIES\' name=\''._TOP_CITIES.'\'>'.$countryname.'</a>';
		}
		
		#new map setup
		$mapname = "map".time().rand(0,1000000);
		?>
		<div  id='<?php echo $mapname; ?>' style="width: 100%; height: <?php echo $customheight; ?>px;"></div>
		<script>
			var visitsData = {
				<?php echo $jmapdata[0]; ?>
			};
			var pageviewsData = {
				<?php echo $jmapdata[1]; ?>
			};
			var ppuData = {
				<?php echo $jmapdata[2]; ?>
			};
			
			var zoomed = 0;
			
			$('#<?php echo $mapname; ?>').vectorMap({
				map: 'world_mill_en',
				zoomOnScroll: true,
				zoomStep: 2,
				zoomMax: 64,
				backgroundColor: '<?php echo $mapbgColor; ?>',
				regionStyle: {
					initial: {
						fill: '#B5B3B3'
					}
				},
				series: {
					regions: [{
						values: visitsData,
						scale: ['<?php echo $this->ColorScaleStart; ?>', '<?php echo $this->ColorScaleEnd; ?>'],
						normalizeFunction: 'polynomial'					
					}]
				},
				hoverOpacity: 0.7,
				hoverColor: false,
				onRegionLabelShow: function(e, el, code){
					if(visitsData[code] == undefined){
						visitsData[code] = 0;
					}
					if(pageviewsData[code] == undefined){
						pageviewsData[code] = 0;
					}	
					if(ppuData[code] == undefined){
						ppuData[code] = 0;
					}						
					el.html(el.html()
						+'<hr/>(<?php echo _VISITS; ?>: '+visitsData[code]
						+')<br/>(<?php echo _PAGEVIEWS; ?>: '+pageviewsData[code]
						+')<br/>(<?php echo _PAGES_PER_VISIT; ?>: '+ppuData[code]+')');
				},
				onRegionClick: function(e,code){
					//
				}
			});		
		</script>		
		<?php
	}

	function WorldMapGraph($data){
		global $lang, $reports, $cnames;
		$temp = [];

		$jmapdata[0] = "";
		$jmapdata[1] = "";
		$jmapdata[2] = "";
		$i = 0;

		$oricnames = array("AF" => "Afghanistan","AL" => "Albania","DZ" => "Algeria","AS" => "American Samoa","AD" => "Andorra","AO" => "Angola","AI" => "Anguilla","AQ" => "Antarctica","AG" => "Antigua And Barbuda","AR" => "Argentina","AM" => "Armenia","AW" => "Aruba","AU" => "Australia","AT" => "Austria","AZ" => "Azerbaijan","BS" => "Bahamas","BH" => "Bahrain","BD" => "Bangladesh","BB" => "Barbados","BY" => "Belarus","BE" => "Belgium","BZ" => "Belize","BJ" => "Benin","BM" => "Bermuda","BT" => "Bhutan","BO" => "Bolivia","BA" => "Bosnia And Herzegowina","BW" => "Botswana","BV" => "Bouvet Island","BR" => "Brazil","IO" => "British Indian Ocean Territory","BN" => "Brunei Darussalam","BG" => "Bulgaria","BF" => "Burkina Faso","BI" => "Burundi","KH" => "Cambodia","CM" => "Cameroon","CA" => "Canada","CV" => "Cape Verde","KY" => "Cayman Islands","CF" => "Central African Republic","TD" => "Chad","CL" => "Chile","CN" => "China","CX" => "Christmas Island","CC" => "Cocos (keeling) Islands","CO" => "Colombia","KM" => "Comoros","CG" => "Congo","CD" => "Congo, The Drc","CK" => "Cook Islands","CR" => "Costa Rica","CI" => "Cote D'ivoire","HR" => "Croatia (local Name: Hrvatska)","CU" => "Cuba","CY" => "Cyprus","CZ" => "Czech Republic","DK" => "Denmark","DJ" => "Djibouti","DM" => "Dominica","DO" => "Dominican Republic","TP" => "East Timor","EC" => "Ecuador","EG" => "Egypt","SV" => "El Salvador","GQ" => "Equatorial Guinea","ER" => "Eritrea","EE" => "Estonia","ET" => "Ethiopia","FK" => "Falkland Islands (malvinas)","FO" => "Faroe Islands","FJ" => "Fiji","FI" => "Finland","FR" => "France","FX" => "France, Metropolitan","GF" => "French Guiana","PF" => "French Polynesia","TF" => "French Southern Territories","GA" => "Gabon","GM" => "Gambia","GE" => "Georgia","DE" => "Germany","GH" => "Ghana","GI" => "Gibraltar","GR" => "Greece","GL" => "Greenland","GD" => "Grenada","GP" => "Guadeloupe","GU" => "Guam","GT" => "Guatemala","GN" => "Guinea","GW" => "Guinea-bissau","GY" => "Guyana","HT" => "Haiti","HM" => "Heard And Mc Donald Islands","VA" => "Holy See (vatican City State)","HN" => "Honduras","HK" => "Hong Kong","HU" => "Hungary","IS" => "Iceland","IN" => "India","ID" => "Indonesia","IR" => "Iran, Islamic Republic of","IQ" => "Iraq","IE" => "Ireland","IL" => "Israel","IT" => "Italy","JM" => "Jamaica","JP" => "Japan","JO" => "Jordan","KZ" => "Kazakhstan","KE" => "Kenya","KI" => "Kiribati","KP" => "Korea, D.p.r.o.","KR" => "Korea, Republic of","KW" => "Kuwait","KG" => "Kyrgyzstan","LA" => "Laos","LV" => "Latvia","LB" => "Lebanon","LS" => "Lesotho","LR" => "Liberia","LY" => "Libyan Arab Jamahiriya","LI" => "Liechtenstein","LT" => "Lithuania","LU" => "Luxembourg","MO" => "Macau","MK" => "Macedonia","MG" => "Madagascar","MW" => "Malawi","MY" => "Malaysia","MV" => "Maldives","ML" => "Mali","MT" => "Malta","MH" => "Marshall Islands","MQ" => "Martinique","MR" => "Mauritania","MU" => "Mauritius","YT" => "Mayotte","MX" => "Mexico","FM" => "Micronesia, Federated States Of","MD" => "Moldova, Republic Of","MC" => "Monaco","MN" => "Mongolia","ME" => "Montenegro","MS" => "Montserrat","MA" => "Morocco","MZ" => "Mozambique","MM" => "Myanmar (burma)","NA" => "Namibia","NR" => "Nauru","NP" => "Nepal","NL" => "Netherlands","AN" => "Netherlands Antilles","NC" => "New Caledonia","NZ" => "New Zealand","NI" => "Nicaragua","NE" => "Niger","NG" => "Nigeria","NU" => "Niue","NF" => "Norfolk Island","MP" => "Northern Mariana Islands","NO" => "Norway","OM" => "Oman","PK" => "Pakistan","PW" => "Palau","PA" => "Panama","PG" => "Papua New Guinea","PY" => "Paraguay","PE" => "Peru","PH" => "Philippines","PN" => "Pitcairn","PL" => "Poland","PT" => "Portugal","PR" => "Puerto Rico","QA" => "Qatar","RE" => "Reunion","RO" => "Romania","RU" => "Russian Federation","RW" => "Rwanda","KN" => "Saint Kitts And Nevis","LC" => "Saint Lucia","VC" => "Saint Vincent And The Grenadines","WS" => "Samoa","SM" => "San Marino","ST" => "Sao Tome And Principe","SA" => "Saudi Arabia","SN" => "Senegal","RS" => "Serbia","SC" => "Seychelles","SL" => "Sierra Leone","SG" => "Singapore","SK" => "Slovakia (slovak Republic)","SI" => "Slovenia","SB" => "Solomon Islands","SO" => "Somalia","ZA" => "South Africa","SS" => "South Sudan","GS" => "South Georgia And South S.s.","ES" => "Spain","LK" => "Sri Lanka","SH" => "St. Helena","PM" => "St. Pierre And Miquelon","SD" => "Sudan","SR" => "Suriname","SJ" => "Svalbard And Jan Mayen Islands","SZ" => "Swaziland","SE" => "Sweden","CH" => "Switzerland","SY" => "Syrian Arab Republic","TW" => "Taiwan, Province Of China","TJ" => "Tajikistan","TZ" => "Tanzania, United Republic Of","TH" => "Thailand","TG" => "Togo","TK" => "Tokelau","TO" => "Tonga","TT" => "Trinidad And Tobago","TN" => "Tunisia","TR" => "Turkey","TM" => "Turkmenistan","TC" => "Turks And Caicos Islands","TV" => "Tuvalu","UG" => "Uganda","UA" => "Ukraine","AE" => "United Arab Emirates","GB" => "United Kingdom","US" => "United States","UM" => "U.s. Minor Islands","UY" => "Uruguay","UZ" => "Uzbekistan","VU" => "Vanuatu","VE" => "Venezuela","VN" => "Viet Nam","VG" => "Virgin Islands (british)","VI" => "Virgin Islands (u.s.)","WF" => "Wallis And Futuna Islands","EH" => "Western Sahara","YE" => "Yemen","ZM" => "Zambia","ZW" => "Zimbabwe");
		$oricnames[_UNKNOWN]="Unknown";
		$oricnames["A1"]=_A1;
		$oricnames["A2"]=_A2;


		foreach($data as $key => $row) {
			// if ( !isset( $oricnames[$row[0]] ) ) {
			// 	echoWarning("No oricname for $row[0]");
			// }
			// if ( !isset( $cnames[$row[0]] ) ) {
			// 	echoWarning("No cname for $row[0]");
			// }
			$countryname = @$oricnames[$row[0]];
			$temp[$countryname] = $data[$key];
			$temp[$countryname][4] = @$cnames[$row[0]];
		}
		
		$data = $temp;

		unset($data[""]);
		unset($data["unknown"]);

		$leafletmapId = "llm" . md5(time()).rand(1, 1000);
		$full_url = "reports.php?conf={$this->profile->profilename}&outputmode=json&labels=_TOP_CITIES&limit={$this->limit}&minimumDate=". urlencode(date(implode($this->profile->dateFormat),$this->from))."&maximumDate=". urlencode(date(implode($this->profile->dateFormat),$this->to)) ."&country=";
		?>
		<div id="<?php echo $leafletmapId; ?>" class="leafletmap"></div>
		<script>var data = <?php echo json_encode($data);?>;</script>
		<!-- world-countries.js was loaded here -->
		<script charset="utf-8" type="text/javascript">

		w =$("#<?php echo $leafletmapId; ?>").width();
		$("#<?php echo $leafletmapId; ?>").height(300);
		var cityMarkers = [];
		var cityData = [];
		var countryIndex = [];
		var countryCityLayers = [];

		var maxCityValue = 0;
		var maxRadius = 20;

		var hoverOverCity = false;
		var hoverOverCountry = false;
		var hoverOverCountryName = "";

		function roundTo(number) {
			number = Math.round(number);
			number = number.toString();
			var to = 5*(Math.pow(10,number.length)/100);

			return Math.round(number / to) * to;
		}
		function radius(value){
			var radius = ((value / this.maxCityValue) * this.maxRadius);
			if(radius < 5){
				return 5;
			}else{
				return radius;
			}
		}

		function cityMarkerscircle(markers,country, map){
			var tempGroup = L.layerGroup();
			for (var i = 0; i < markers.length; i++) {
				var marker = new L.CircleMarker(markers[i].latlng);
				marker.setRadius(radius(markers[i].value));
				marker.on('mouseover', cityFeature);
				marker.on('click', zoomToCity);
				marker.on('mouseout', infoUpdate);
				tempGroup.addLayer(marker);
			}
			countryCityLayers[countryIndex.length] = tempGroup;
			countryIndex[countryIndex.length] = country+"Layer";
			cities.addLayer(tempGroup);

		}

		//var cloudmadeUrl = 'http://otile{s}.mqcdn.com/tiles/1.0.0/map/{z}/{x}/{y}.png';
		//var cloudmadeAttribution = ' Tiles Courtesy of <a href="http://www.mapquest.com/" target="_blank">MapQuest</a> <img src="http://developer.mapquest.com/content/osm/mq_logo.png">';
		//var cloudmadeUrl = 'http://a.tile.openstreetmap.org/{z}/{x}/{y}.png';
	
		//var cloudmadeUrl = 'http://{s}.basemaps.cartocdn.com/light_all/{z}/{x}/{y}.png';
		//var cloudmadeAttribution = '&copy; <a href="http://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors, &copy; <a href="https://cartodb.com/attributions">CartoDB</a>';
		var cloudmadeUrl = 'https://tile.stamen.com/toner-lite/{z}/{x}/{y}.png';
		var cloudmadeAttribution = '<a target="_top" href="https:///stamen.com">Stamen Design</a>, under <a target="_top" hrefhttps://://creativecommons.org/licenses/by/3.0">CC BY 3.0</a>. Data by <a target="_top" hrhttps://ps://openstreetmap.org">OpenStreetMap</a>, under <a target="_top" https://ttps://creativecommons.org/licenses/by-sa/3.0">CC BY SA</a>';

		if (document.location.protocol=="https:") { 
			var cloudmadeUrl = 'https://stamen-tiles.a.ssl.fastly.net/toner-lite/{z}/{x}/{y}.png';
		} 
					
		var minimal   = L.tileLayer(cloudmadeUrl, {styleId: 22677, attribution: cloudmadeAttribution,worldCopyJump: true,
			noWrap: false });

		var map = L.map('<?php echo $leafletmapId; ?>', {center: [25.505, 15], zoom: 1, worldCopyJump: true, layers: [minimal], minZoom: 1, maxZoom: 50 });
		
		var cities = L.layerGroup();

		var maxScale = 0;
		for (var i = 0; i < worldData.features.length; i++) {
			var props = worldData.features[i].properties;
			props.ccode = (data[props.name] != undefined)? data[props.name][0] : 'Unknown'; 
			props.view_name = (data[props.name] != undefined)? data[props.name][4] : props.name; 
			props.visits = (data[props.name] != undefined)? data[props.name][1] : 0;
			props.pageviews = (data[props.name] != undefined)? data[props.name][2] : 0;
			props.ppv = (data[props.name] != undefined)? data[props.name][3] : 0;
			if(props.visits > maxScale){
				maxScale = props.visits;
			}
		}
		var baseLayers = {
			"Minimal": minimal
		};

		//Control that shows state info on hover
		var loading = L.control({position: "topright"});
		var info = L.control();
		

		loading.onAdd = function (map) {
			var div = L.DomUtil.create('div', 'loading');
			div.innerHTML = "Loading...";			
			return div;
		}


		info.onAdd = function (map) {
			this._div = L.DomUtil.create('div', 'info');
			this.update();
			return this._div;
		};

		info.update = function (props,sw) {
			if(sw == "countryMouseIn"){
				this._div.innerHTML = '<h4>World Wide Statistics</h4>' +  (props ?
					'<b>' + props.view_name + '</b><br />Visits: ' + props.visits + '<br />Pageviews: ' + props.pageviews + '<br />Pages Per Visit: ' + props.ppv
					: 'Hover over a country');
				hoverOverCountry = true;
				if(map.hasLayer(cities)){
					for (var i = 0; i < countryIndex.length; i++) {
						if(countryIndex[i] == hoverOverCountryName + "Layer"){
							if(cities.hasLayer(countryCityLayers[i])){
								cities.removeLayer(countryCityLayers[i]);
							}
						}
					}
				}
				hoverOverCountryName = props.ccode;
				if(props != undefined && map.hasLayer(cities)){
					if(cityMarkers[props.ccode] == undefined){
						cityMarkers[props.ccode] = [];
						// load markers for this city
						$(".leafletmap .loading").show();
						loading._container.innerHTML = "Loading City Data...";
						getCountryCities(props.ccode);
						

						for (var i = 0; i < countryIndex.length; i++) {
							if(countryIndex[i] == props.ccode + "Layer"){
								cities.addLayer(countryCityLayers[i]);
								break;
							}
						}
					} else {
						if(map.hasLayer(cities)){
							for (var i = 0; i < countryIndex.length; i++) {
								if(countryIndex[i] == props.ccode + "Layer"){
									cities.addLayer(countryCityLayers[i]);
									break;
								}
							}
						}
					}
				}else{

				}
			}else if(sw == "cityMouseIn"){
				this._div.innerHTML = '<h4>Country Wide Statistics</h4>' +  (props ?
					'<b>' + props['name'] + '</b><br />Unique Visitors: ' + props['value']
					: 'Hover over a city');
				if(map.hasLayer(cities)){
					for (var i = 0; i < countryIndex.length; i++) {
						if(countryIndex[i] == props['country'] + "Layer"){
							cities.addLayer(countryCityLayers[i]);
							hoverOverCity = true;
							break;
						}
					}
				}
			}else if(sw == "cityMouseOut"){
				hoverOverCity = false;
				
			}else if(sw == "countryMouseOut") {

				if(map.getZoom() >= 7){
					this._div.innerHTML = '<h4>Country Wide Statistics</h4>Hover over a city';
				} else if (map.getZoom() < 7 && map.getZoom() >= 1){
					if(map.hasLayer(cities) && hoverOverCity === false){
						this._div.innerHTML = '<h4>World Wide Statistics</h4>Hover over a country';
						cities.clearLayers();
					}
				}
			} else {
				this._div.innerHTML = '<h4>World Wide Statistics</h4>Hover over a country';
			}

		};


		function CheckValidMarkerName(name){
			if(name == undefined){
				return false;
			}else if(name == "unknown"){
				return false;
			}else if(name == "No data for this date range"){
				return false;
			}
			return true;
		}
		function CheckValidMarkerLatlng(lat,lng){
			if(lat < -90 || lat > 90 || lng < -90 || lng > 90){
				return true;
			}
			return true;
		}

		// add the data to the map
		function AddCitiesToMap(country,citydata){			
			try{
				var citiesdata = JSON.parse(citydata);
				//
				// add a different counter for akward result in array indexing
				var c = 0;
				this.maxCityValue = 0;
				for(var i = 0; i < citiesdata.length; i++){
					if(CheckValidMarkerName(citiesdata[i][0]) === true && CheckValidMarkerLatlng(citiesdata[i][3],citiesdata[i][2]) === true){
						if(this.maxCityValue < citiesdata[i][1]){
							this.maxCityValue = citiesdata[i][1];
						}
						var city = {};
						city.latlng = [citiesdata[i][3],citiesdata[i][2]];
						city.name = citiesdata[i][0] + " - " + country;
						city.country = country;
						city.value = citiesdata[i][1];
						cityMarkers[country][c] = city;
						cityData[cityData.length] = city;
						c ++;
					}
				}
				cityMarkerscircle(cityMarkers[country],country, map);

			}catch(err){
				// error in corrupt city data
				// something somewhere went teribbly wrong, but thats okay :)
			}
		}

		// Does an ajax request for the cities.
		function getCountryCities(country){
			$.ajax({
				type: "POST",
				url: '<?php echo $full_url; ?>'+country,
				success: function(result) {
					loading._container.innerHTML = "";
					$(".leafletmap .loading").hide();
					AddCitiesToMap(country,result);
				}
			});
		}
		
		info.addTo(map);


		// get color depending on population visits value
		function getColor(d) {
			var colors = ["#DF1B35","#d53e4f","#f46d43","#FF831F","#fdae61","#FFD75F","#fee08b","#ffffbf","#ffffff", "#e6f598","#ffffff","#66c2a5","#3288bd"];
			


			return d > roundTo(maxScale/8*7) ? colors[0] :
				   d > roundTo(maxScale/8*4.8)  ? colors[1] :
				   d > roundTo(maxScale/8*3)  ? colors[2] :
				   d > roundTo(maxScale/8*2)  ? colors[3] :
				   d > roundTo(maxScale/8*1.2)   ? colors[4] :
				   d > roundTo(maxScale/8*0.7)   ? colors[5] :
				   d > roundTo(maxScale/8*0.5)   ? colors[6] :
				   d >= 1 ? colors[7] :	colors[8];

		}

		function style(feature) {
			if(feature.properties.visits == 0){
				return {
					weight: 1,
					opacity: 1,
					color: '#777777',
					dashArray: '',
					fillOpacity: 0.5,
					fillColor: getColor(feature.properties.visits)
				};
			}else{
				return {
					weight: 1,
					opacity: 1,
					color: '#777777',
					dashArray: '',
					fillOpacity: 0.7,
					fillColor: getColor(feature.properties.visits)
				};
			}
		}
		function highlightFeature(e) {
			var layer = e.target;
			if(map.hasLayer(geojson)){
				if(layer.feature.properties.visits == 0){
					layer.setStyle({
						weight: 1,
						color: 'grey',
						dashArray: '',
						fillOpacity: 0.0

					});
				}else{
					layer.setStyle({
						weight: 2,
						color: getColor(layer.feature.properties.visits),
						dashArray: '',
						fillOpacity: 0.4
					});
				}
			}

			if(map.hasLayer(geojson)){
				info.update(layer.feature.properties,"countryMouseIn");
			}else{
			}
		}

		var geojson;

		function timer(time,update,complete) {
			var start = new Date().getTime();
			var interval = setInterval(function() {
				var now = time-(new Date().getTime()-start);
				if( now <= 0) {
					clearInterval(interval);
					if(hoverOverCountry === false){
						complete();
					}
				}
				else{ 
					update(Math.floor(now/1000));
				}
			},100); // the smaller this number, the more accurate the timer will be
		}

		function resetHighlight(e) {
			if(map.hasLayer(geojson)){
				geojson.resetStyle(e.target);
			}
			if(map.hasLayer(cities)){
				hoverOverCountry = false;
				timer(
					1, // milliseconds
					function(timeleft) { // called every step to update the visible countdown

					},
					function() { // what to do after
						info.update( null,"countryMouseOut");
					}
				);
			}
		}

		function zoomToCountry(e) {
			map.fitBounds(e.target.getBounds());
		}
		function zoomToCity(e) {
			map.setView( e.target.getLatLng(), 9);
		}
		var citiesOn = 0;
		var zoomedIn = 0;
		map.on('zoomend', function(e) {
		
			if(map.getZoom() > 4){	
				if(!map.hasLayer(minimal)){
					map.addLayer(minimal);
				}
			} 
			if(map.getZoom() >= 8){	
				
				if(map.hasLayer(cities) && map.hasLayer(geojson) && citiesOn != 1){
					citiesOn = 1;
				}
				map.removeLayer(geojson);
				if(citiesOn == 0){
					if(cityMarkers[hoverOverCountryName] == undefined){
						cityMarkers[hoverOverCountryName] = [];
						getCountryCities(hoverOverCountryName);
					}
					for (var i = 0; i < countryIndex.length; i++) {
						if(countryIndex[i] == hoverOverCountryName + "Layer"){
							cities.addLayer(countryCityLayers[i]);
						}
					}
					map.addLayer(cities);
				}
			} else if (map.getZoom() < 8 && map.getZoom() >= 1){
				if(map.hasLayer(cities) && map.hasLayer(geojson) && citiesOn != 1){
					citiesOn = 1;
				}else if(map.hasLayer(cities) && map.hasLayer(geojson) && citiesOn == 1){
					map.addLayer(geojson);
					map.removeLayer(cities);
					map.addLayer(cities);
				}else if(!map.hasLayer(cities) && map.hasLayer(geojson) && citiesOn == 1){
					map.addLayer(geojson);
					map.removeLayer(cities);
					citiesOn = 0;
				}else if(map.hasLayer(cities) && !map.hasLayer(geojson) && citiesOn == 1){
					map.addLayer(geojson);
					map.addLayer(cities);
				}else if(map.hasLayer(cities) && !map.hasLayer(geojson) && citiesOn == 0){
					map.addLayer(geojson);
					cities.clearLayers();
					map.removeLayer(cities);
				}else if(!map.hasLayer(cities) && !map.hasLayer(geojson) && citiesOn == 0){
					map.addLayer(geojson);
				}
			}

			if(map.getZoom() == 1){
				map.removeLayer(minimal);
				map.setView([45.505, 0], 1);

			}
			
		});
		
		function cityFeature(e){
			var layer = e.target;
			for (var i = 0; i < cityData.length; i++) {
				if((layer.getLatLng()['lat'] == cityData[i]['latlng'][0]) && (layer.getLatLng()['lng'] == cityData[i]['latlng'][1])){
					info.update(cityData[i],"cityMouseIn");
					break;
				}
			}
		}
		function infoUpdate(){
			info.update( null, "cityMouseOut");
		}

		function onEachFeature(feature, layer) {
			layer.on({
				mouseover: highlightFeature,
				mouseout: resetHighlight,
				click: zoomToCountry
			});
		}

		geojson = L.geoJson(worldData, {
			style: style,
			onEachFeature: onEachFeature
		});


		var overlayMaps = {
			"Map":minimal,
			"Heatmap":geojson,
			"Cities": cities
		};

		L.control.layers(null,overlayMaps,{position: 'bottomright', autoZIndex: false, collapsed: false}).addTo(map);
		loading.addTo(map);
		$(".leafletmap .loading").hide();
		
		if(map.getZoom() >= 5){
			map.removeLayer(geojson);			
			map.addLayer(cities);
			map.addLayer(minimal);
		} else if (map.getZoom() < 5 && map.getZoom() >= 1){
			map.addLayer(geojson);
			map.removeLayer(cities);
			map.removeLayer(minimal);
		}
		
		var legend = L.control({position: 'bottomleft', autoZIndex: false});

		legend.onAdd = function (map) {
			if(maxScale == 0 || maxScale == undefined){
				var div = L.DomUtil.create('div', 'info legend');
				div.innerHTML = "There is no data in this daterange!";
			}else{
				var div = L.DomUtil.create('div', 'info legend'),
					grades = [1, roundTo(maxScale/8*0.5), roundTo(maxScale/8*0.7), roundTo(maxScale/8*1.2), roundTo(maxScale/8*2), roundTo(maxScale/8*3),  roundTo(maxScale/8*4.8), roundTo(maxScale/8*7)],
					labels = [],
					from, to;

				for (var i = 0; i < grades.length; i++) {
					from = grades[i];
					to = grades[i + 1];

					labels.push(
						'<i style="background:' + getColor(from + 1) + '"></i> ' +
						from + (to ? ' - ' + to : '+'));
				}

				div.innerHTML = labels.join('<br>');
			}
			return div;
		};

		legend.addTo(map);


		</script>		
		<?php
	}	
	
	function chordGraphCreate(){ 
		$chordGraphID = "c".md5(time()).rand(1,1000);

		$pages = $this->Datamatrix();
		if(empty($pages)){
			echoNotice(_NO_DATA_TO_DISPLAY,"margin:5px;");
			return;
		}
		?><script><?php echo $this->echoMatrix; ?>

		var width = ($("#<?php echo $chordGraphID; ?>").closest(".report").outerWidth() - 350),
			height = width,
			outerRadius = Math.min(width, height) / 2 - 12,
			innerRadius = outerRadius - 20;

		var arc = d3.svg.arc()
			.innerRadius(innerRadius)
			.outerRadius(outerRadius);

		var layout = d3.layout.chord()
			.padding(.04)
			.sortSubgroups(d3.descending)
			.sortChords(d3.ascending);

		var path = d3.svg.chord()
			.radius(innerRadius);

		  var svg = d3.select("#<?php echo $chordGraphID; ?>").append("svg")
			.attr("width", width)
			.attr("height", height)
		  .append("g")
			.attr("class", "circle")
			.attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");
		
		svg.append("circle")
			.attr("r", outerRadius);

		
		// Compute the chord layout.
		layout.matrix(matrix);

		// Add a group per neighborhood.
		var group = svg.selectAll(".group")
			.data(layout.groups)
			.enter().append("g")
			.attr("class", "group")
			.on("mouseover", mouseover);

		// Add a mouseover title.
		group.append("title").text(function(d, i) {
			return "Page: " + pages.page[i] + "\nPageviews: " + Math.round(d.value);
		});

		// Add the group arc.
		var groupPath = group.append("path")
			.attr("id", function(d, i) { return "group" + i; })
			.attr("d", arc)
			.style("fill", function(d, i) { return pages.color[i]; });

		 // Add a text label.
		var groupText = group.append("text")
			.attr("x", 6)
			.attr("dy", 15);

		groupText.append("textPath")
			.attr("xlink:href", function(d, i) { return "#group" + i; })
			.text(function(d, i) { return (pages.page[i] != "/") ? pages.page[i] : "/ (Home Page)"; });

		// Remove the labels that don't fit. :(
		groupText.filter(function(d, i) { return groupPath[0][i].getTotalLength() / 2 - 16 < this.getComputedTextLength(); })
			.remove();

		// Add the chords.
		var chord = svg.selectAll(".chord")
			.data(layout.chords)
			.enter().append("path")
			.attr("class", "chord")
			.style("fill", function(d) { return pages.color[d.source.index]; })
			.attr("d", path);

		// Add an elaborate mouseover title for each chord.
		chord.append("title").text(function(d) {
			return pages.page[d.source.index]
			  + " → " + pages.page[d.target.index]
			  + ": " + Math.round(d.source.value)
			  + "\n" + pages.page[d.target.index]
			  + " → " + pages.page[d.source.index]
			  + ": " + Math.round(d.target.value);
		});

		function mouseover(d, i) {
			chord.classed("fade", function(p) {
				return p.source.index != i
					&& p.target.index != i;
			});
		}
		

		</script>
		<?php

		# Legend style and position
		$legendcontainerstyle = "padding-top: 15px; margin: 0px 0px 0px 0px; display:block;";
		$legendstyle = "display: block; clear: both;";

		?>
			<div id="<?php echo $chordGraphID; ?>" class="chordgraph"></div>
			<div class="chordlegend">
		<?php
			#Creating legend
			echo $this->CreateLegend($pages, $legendcontainerstyle, $legendstyle, 28);
		?>
			</div>
		<?php
	  }

	function Datamatrix() {
		global $db, $reports;
		$limit = $this->limit;
		# start with a seed page (like the homepage)
		$ref = "https://" .$this->profile->confdomain.$this->source;
		$seed = getID($ref,"referrers");
		$url = getID($this->source,"urls");

		# then get a a top list of pages where the seed page is the referrer
		$q = "select url, count(*) as hits from {$this->profile->tablename} where timestamp between ".$db->Quote($this->from)." and ".$db->Quote($this->to)." and referrer = '$seed' group by url order by hits desc limit {$limit}";
		$q = $db->Execute($q);

		# get the url ids and set up the matrix
		$urls = [];
		$pages = [];
		$i = 0;
		while ($data = $q->FetchRow()) { 
			# get the url id
			$urlid = $data['url'];
			$urls[$urlid] = $i;
			# get page labels
			$pages[$i] = getVAL($urlid,"urls");
			$i++;
		}

		if(empty($pages)){
			return [];
		}

		# Separate labels for the legend
		$pageslegend = $pages;
		if(count($pages) > $this->limit){
			$this->limit = count($pages);
			$limit = count($pages);
		}

		# Getting colors for the graph
		$allcolors = $this->GetGraphColors($pages);
		$colors = [];
		for($i = 0;$i < $limit; $i++){
			$colors[] = $allcolors[$i];
		}

		$urlsql = array_flip($urls);
		$hiddenlegends = $this->getHiddenlegends();
		# The logic for hidding data through the legend without screwing up the colors
		if(isset($hiddenlegends[0])&& count($hiddenlegends[0]) != 0){

			for($i = 0;$i < count($hiddenlegends[0]); $i++){
				unset($urlsql[$hiddenlegends[0][$i]]);
				unset($pages[$hiddenlegends[0][$i]]);
				unset($colors[$hiddenlegends[0][$i]]);
			}
			$temp1 = $urlsql;
			$temp2 = $pages;
			$temp3 = $colors;
			$urlsql = [];
			$pages = [];
			$colors = [];
			for($i = 0; $i < $limit; $i++){
				if(!empty($temp1[$i])){
					$urlsql[] = $temp1[$i];
					$pages[] = $temp2[$i];
					$colors[] = $temp3[$i];
				}
			}
			$urls = array_flip($urlsql);
			$limit -= count($hiddenlegends[0]);
		}else{
			$urlsql = array_flip($urls);
		}
		
		$urlsql = implode(',', $urlsql);
		$matrix = [];
		

		# for each url as a referrer, get the number of clicks to the other urls
		$i =0;
		foreach ($urls as $uid => $p) {
		  
			#get the referrerid
			$refid = $this->urlid_to_refid($uid);
				
			#create the query
			$q = "select url, count(*) as hits from {$this->profile->tablename} where timestamp between ".$db->Quote($this->from)." and ".$db->Quote($this->to)." and referrer = '$refid' and url IN ($urlsql) group by url order by url";
			$q = $db->Execute($q);

			# fill the matrix
			while ($data = $q->FetchRow()) {
				$u = $data['url'];
				$h = $data['hits'];
				$pos = $urls[$u];
				$matrix[$i][$pos] = $h;
			}	
			$i++;
		}
		
		# now echo the matrix in the required order
		$this->echoMatrix = "var matrix = [\n";
		$i = 0;
		while ($i < $limit) {
			$c = 0;
			$row = [];
			while ($c < $limit) {
				if (isset($matrix[$i][$c]) && $i == $c){
					$row[] = "0";
				}else if (isset($matrix[$i][$c])) {
					$row[] = $matrix[$i][$c];
				} else {
					$row[] = "0";
				}
				$c++;        
			}
			$this->echoMatrix .= "[" .implode(",", $row). "],";
			$this->echoMatrix .= "\n";
			$i++;
		}
		$this->echoMatrix .= "];";
		$this->echoMatrix .= "var pages = new Object();";
		$this->echoMatrix .= "pages.page = ". json_encode($pages) .";";
		$this->echoMatrix .= "pages.color = ". json_encode($colors) .";";
		return $pageslegend;
	}

	function HorizontalGraph($data){
		# Create a unique id for our graph
		$md5_time = md5(time()).rand(1,1000);
		
		if(empty($data)) {
			echoNotice(_NO_DATA_TO_DISPLAY." "._CHECK_SETTINGS_FOR_REPORT,"margin:10px;");
			return; 
		}
		
		$legends = [];
		foreach ($this->columnDefinitions as $key => $value) {
			$legends[] = addslashes(utf8_encode($value["Label"]));
		}
		unset($legends[0]);
		$colors = $this->GetGraphColors($legends);
		
		$total = 0;
		foreach($data as $group => $v){
			$rows = count($legends);
			$bars = [];

			for($i = 1; $i <= $rows; $i++){
				$v[$i] =  (!empty($v[$i])) ? $v[$i] : 0;
				$bars[$i] = "[ $v[$i] , '$group' ]";
				$total ++;
			}
			ksort($bars);
			$days[] = $bars;
		}
		
		$data = [];
		$num_groups = count($days);

		$g = 1;
		$e = 1;
		for($i = 1; $i <= $total; $i++){
			$calc = ( $total / $num_groups ) * $g ;

			$data[$e][] = $days[ ($g - 1) ][$e];
			
			if($g == $num_groups){
				$g = 1;

				if($i == $calc){
					$e = 1;					
				} else {
					$e++;
				}

			} else {
				$g ++;	
			}

		}
		
		# Build some freaking labels
		$g_labels = [];
		for($i = 1; $i <= count($legends); $i++){
			
			$gs = [];
			for($g = 1; $g <= $num_groups; $g++){
				$gs[] = " '{$legends[$i]}' ";
			}
			
			$g_labels[] = "{ pointLabels:{ labels:[" . implode(",",$gs) . "], location:'e', show: true, edgeTolerance: -50, hideZeros: false } }";	
		}

		$graph_js = [];
		foreach($data as $set){
			$graph_js[] = "[". implode(",",$set) . "]";
		}
		$graph_js  = implode(",",$graph_js);
		$color_js = implode("','", $colors);
		?>

		<script>
			$(document).ready(function(){
				var gdata = [<?php echo $graph_js; ?>];
				

				var plot2 = $.jqplot('<?php echo $md5_time; ?>', gdata, {
					seriesColors: ['<?php echo $color_js; ?>'],
					seriesDefaults: {
						renderer:$.jqplot.BarRenderer,
						// Show point labels to the right ('e'ast) of each bar.
						// edgeTolerance of -15 allows labels flow outside the grid
						// up to 15 pixels.  If they flow out more than that, they 
						// will be hidden.
						// Rotate the bar shadow as if bar is lit from top right.
						shadowAngle: 135,

						
						// Here's where we tell the chart it is oriented horizontally.
						rendererOptions: {
							waterfall: false,
							barDirection: 'horizontal',
							barPadding: 3,
							barMargin: 3,
							barWidth: null,
							animation: {
								speed: 100
							},
							smooth: true
						},
						trendline: { show: false}
					},
					highlighter: {
						show: false
					},
					cursor: {
						show: false,
						style: 'default',
						zoom: false,
						showTooltip: false,
						showVerticalLine: false
					},
					grid: {
						shadow: false,
						gridLineColor: 'rgba(239,239,239,1)',
						borderWidth: 1,
						borderColor: 'rgba(239,239,239,1)',
						backgroundColor: 'rgba(255,255,255,1)'
					},
					series: [
						<?php echo implode(",", $g_labels); ?>
					],
					axes: {
						yaxis: {
							renderer: $.jqplot.CategoryAxisRenderer,
							tickRenderer: $.jqplot.CanvasAxisTickRenderer,
							ticks: {}
						},
						xaxis: {
							ticks: {}
						}
					}
				});
				var loffset = $("#<?php echo $md5_time; ?> .jqplot-series-canvas").css("left");
				$("#<?php echo $md5_time; ?> .jqplot-point-label").css("left", loffset);
			});
		</script>
		<div class='graph_area' style='margin-top: 20px;'>
			<div id='<?php echo $md5_time; ?>' class='graphcontainer' >
			</div>
		</div>
		<?php

	}

	function Hbar($limit, $data) {
		$md5_time = md5(time());
		$container_id = 'hb'.$md5_time.rand(1,1000);		

		foreach ($this->columnDefinitions as $key => $value) {
			$this->columnDefinitions[$key]["name"] = constant($value["Label"]);
		}
		
		echo "<div id='$container_id' class='hbar' data='".json_encode($data)."' limit='{$this->limit}' coldefs='".json_encode($this->columnDefinitions)."'></div>";

		echo "<script>";
		echo "Hbar.display('$container_id');";
		echo "</script>";
	}

	function LineChart($data) {
		$md5_time = md5(time());
		$container_id = 'lc'.$md5_time.rand(1,1000);		

		$json_data = [];
		foreach ($this->columnDefinitions as $key => $value) {
			if (defined($value["Label"])) {
				$this->columnDefinitions[$key]["name"] = str_replace("'","&rsquo;", constant($value["Label"]));
			} else {
				$this->columnDefinitions[$key]["name"] = $value["Label"];
			}
			if ($key!=0) {
				if  (@$this->columnDefinitions[$key]["show_line"]===false) {
					continue;
				}
				if (isset($this->columnDefinitions[$key]["line_order"])) {
					$k = $this->columnDefinitions[$key]["line_order"];
				} else {
					$k = $key - 1;
				}
				$json_data[$k]["key"] = $this->columnDefinitions[$key]["name"];
				$json_data[$k]["area"] = true;
				$json_data[$k]["disabled"] =  (@$this->columnDefinitions[$key]["disable_line"] == "true" ? true : false);

			}
		}


		$x_ticks = [];
		$i = 0;
		foreach($data as $d){
			foreach ($this->columnDefinitions as $key => $value) {
				if  (@$this->columnDefinitions[$key]["show_line"]===false) {
					continue;
				}

				if ($key==0) {
					$time = false;
					if($this->columnDefinitions[$key]["Label"] == "_DATE" && $this->allowDateFormat){
						$time = strtotime($d[0]);
					}

					if(!$time){ # If strtotime fails (false) then we can set the current value
						$x_ticks[] = $d[0];
					} else {						
						$x_ticks[] = LogaDate($this->profile->dateFormat, $time);
					}
				} else{
					
					if (isset($this->columnDefinitions[$key]["line_order"])) {
						$k = $this->columnDefinitions[$key]["line_order"];
					} else {
						$k = $key - 1;
					}
					$json_data[$k]["values"][] = array( $i ,  (float)$d[$key] );
					
				}

			}
			$i ++;
		}

		ksort($json_data);
		$json_data = array_values( $json_data );
		echo "<div id='$container_id' class='linechart' data-ticks='". json_encode($x_ticks) ."' data-chartoptions='".json_encode($this->chartoptions)."' data-chart='".json_encode($json_data)."' data-limit='".$this->limit."' data-coldefs='".json_encode($this->columnDefinitions)."' >";
		echo "</div>";


		echo "<script>";
		echo "$(document).ready(function(){  lg.LineChart('$container_id'); });";
		echo "</script>";
	}


	function BarChart($data) {
		$md5_time = md5(time());
		$container_id = 'bc'.$md5_time.rand(1,1000);
				
		$json_data = [];
		
		# create one element per serie of data
		foreach ($this->columnDefinitions as $key => $value) {	
			if  (@$value["show_line"] === false) {
				continue;
			}

			if ($key==0) { continue; }

			if (defined($value['Label'])) {
				$label = constant($value['Label']);
			} else {
				$label = $value['Label'];
			}

			if (isset($value["line_order"])) {
				$k = $value["line_order"];
			} else {
				$k = $key - 1;
			}

			$json_data[$k] = array("key" => $label, "values" => []);

			$json_data[$k]["disabled"] = (@$value["disable_line"] == "true" ? true : false);
		}

		ksort($json_data);

		$json_data = array_values( $json_data );

		$fields = $this->fieldsarray();

		# create a vals array, one for each serie of data
		$x_ticks = [];
		$i = 0;

		$vals = [];
		foreach($data as $d){			

			foreach ($this->columnDefinitions as $key => $value) {
				if  (@$this->columnDefinitions[$key]["show_line"]===false) {
					continue;
				}			

				if ($key==0) { 

					$time = false;
					if($this->columnDefinitions[$key]["Label"] == "_DATE" && $this->allowDateFormat){
						$time = strtotime($d[0]);
					}

					if(!$time){ # If strtotime fails (false) then we can set the current value
						$x_ticks[] = $d[0];
					} else {
						$x_ticks[] = LogaDate($this->profile->dateFormat, $time);
					}
					continue;
				}

				if (isset($this->columnDefinitions[$key]["line_order"])) {
					$k = $this->columnDefinitions[$key]["line_order"];
				} else {
					$k = $key - 1;
				}

				$vals[$k][] = array('x' => $i, 'y' => intval($d[$key]) );
			}
			$i ++;
		}

		ksort($vals);
		$vals = array_values( $vals );

		# merge the vals array info the json_data array
		foreach($json_data as $k => $v) {
			$json_data[$k]['values'] = $vals[$k];
		}
		
		// add colors if they are defined
		$colors = "";
		if (isset($this->barchartcolors)) {
			$colors = "data-colors='".json_encode(@$this->barchartcolors)."'";
		}

		$controlLabels = array('stacked' => _STACKED, 'grouped' => _GROUPED);

		echo "<div id='$container_id' class='barchart' $colors data-chart='".json_encode($json_data)."' data-ticks='". json_encode($x_ticks) ."' data-limit='".$this->limit."' data-coldefs='".trim(json_encode($this->columnDefinitions))."' data-chartoptions='".json_encode($this->chartoptions)."' data-controlLabels='".json_encode($controlLabels)."'></div>";		

		if ($this->stacked == true) {
			$s = "true";
		} else {
			$s = "false";
		}

		echo "<script>";
		echo "$(document).ready(function(){  lg.BarChart('$container_id', $s); });";
		echo "</script>";
	}

	function BarLineChart($data) {
		$md5_time = md5(time());
		$container_id = 'blc'.$md5_time.rand(1,1000);
		
		//dump($data);
		$json_data = [];
		$i=0;
		# create one element per serie of data
		foreach ($this->columnDefinitions as $key => $value) {			
			if ($key==0) { continue; }
			if (defined($value['Label'])) {
				$label = constant($value['Label']);
			} else {
				$label = $value['Label'];
			}
			if ($key==1) {
				$json_data[$i] = array("key" => $label, "bar" => true, "values" => []);
			} else {
				$json_data[$i] = array("key" => $label, "values" => []);
			}
			
			$i++;

		}

		# create a vals array, one for each serie of data
		$vals = [];
		foreach($data as $d){

			foreach($d as $k => $v) {
				if ($k==0) { $x = strtotime($v)*1000; continue; }				
				$vals[$k-1][] = array("x" => $x, "y" => $v);
			}
		}

		# merge the vals array info the json_data array
		foreach($json_data as $k => $v) {
			$json_data[$k]['values'] = $vals[$k];
		}
				
		echo "<div id='$container_id' class='barlinechart' data='".json_encode($json_data)."' limit='".$this->limit."' coldefs='".json_encode($this->columnDefinitions)."' data-chartoptions='".json_encode($this->chartoptions)."'>";
		echo "</div>";

		if ($this->stacked == true) {
			$s = "true";
		} else {
			$s = "false";
		}

		echo "<script>";
		echo "lg.BarLineChart('$container_id', $s);";
		echo "</script>";
	}

	function MultiChart($data) {
		$md5_time = md5(time());
		$container_id = 'blc'.$md5_time.rand(1,1000);
		
		//dump($data);
		$json_data = [];
		$i=0;
		# create one element per serie of data
		foreach ($this->columnDefinitions as $key => $value) {			
			if ($key==0) { continue; }
			if (defined($value['Label'])) {
				$label = constant($value['Label']);
			} else {
				$label = $value['Label'];
			}
			if (!isset($value['yAxis'])) {
				$value['yAxis'] =1;
			}
			$json_data[$i] = array("key" => $label, "type" => $value['chart'], "yAxis" => $value['yAxis'],"values" => []);
			
			$i++;

		}

		# create a vals array, one for each serie of data
		$vals = [];
		$x_ticks = [];

		$max = 0;
		$i = 0;
		foreach($data as $d){
			foreach($d as $k => $v) {
				if ($k==0) { 
					$time = false;
					if($this->columnDefinitions[$k]['Label'] == "_DATE" && $this->allowDateFormat){
						$time = strtotime($v);
					}

					if(!$time){ # If strtotime fails (false) then we can set the current value
						$x_ticks[] = $v;
					} else {
						$x_ticks[] = LogaDate($this->profile->dateFormat, $time);
					}
					continue;
				}

				$vals[$k-1][] = array("x" => $i, "y" => $v);
				if ($v > $max) {
					$max = $v;
				}
			}
			$i ++;
		}

		# merge the vals array info the json_data array
		foreach($json_data as $k => $v) {
			$json_data[$k]['values'] = $vals[$k];
		}

		// add colors if they are defined
		$colors = "";
		if (isset($this->barchartcolors)) {
			$colors = "data-colors='".json_encode(@$this->barchartcolors)."'";
		}
				
		echo "<div id='$container_id' class='barlinechart' $colors data-chartoptions='".json_encode($this->chartoptions)."'' data-ticks='". json_encode($x_ticks) ."' data-max='$max' data-chart='".json_encode($json_data)."' limit='".$this->limit."' coldefs='".json_encode($this->columnDefinitions)."'>";
		echo "</div>";

		if ($this->stacked == true) {
			$s = "true";
		} else {
			$s = "false";
		}

		echo "<script>";
		echo "lg.MultiChart('$container_id');";
		echo "</script>";
	}

	function Pie($data) {

		$md5_time = md5(time());
		$container_id = 'hb'.$md5_time.rand(1,1000);
		echo "<div id='$container_id' class='pie' data='".json_encode($data)."' limit='$this->limit' coldefs='".json_encode($this->columnDefinitions)."'>";
			
		echo "</div>";
		echo "<script>";
		echo "lg.Pie('$container_id');";
		echo "</script>";	
	}
	

/*
	**	Convert Trend Data array to nvd3 Stacked Area array
		- column 0 must be a date it is an x axes
	**	- $sort_key 		=> sort the value on this column
	**	- $column_header 	=> the key of the column header
	**	- $value_key 		=> the key of the value
	*/
	function ConvertToStackedAreaData($data, $column_header, $x_key, $y_key, $sort_key){
		$converted = [];
		$i = 0;
		foreach ($data as $row) {

			//$k = $row[$sort_key];
			$k=$i;
			$converted[$k]['key'] = $row[$column_header];

			# add the values to the array and make sure the value data is an int or else the graph will be blown up
			//$json_data[$k]["values"][] = array($t, (int)$row[$y_key]);
			// $converted[$k]['values'][] = array( 
			// 	"label" => $row[$x_key],
			// 	"value" => (int)$row[$y_key]
			// );
			if (!is_int($row[$x_key]))  {
				$row[$x_key] = (strtotime($row[$x_key]) * 1000);
			}
			$converted[$k]['values'][] = array( $row[$x_key], (int)$row[$y_key] ); 
			$i ++;
		}
		return $converted;
	}

	/*
	**	FlattenTrendData() Changes a data set from getTrendDataFromFiles to a normal GetReportData data set
	**	Array(
		[20140824] => Array(
			[0] => Array(
					[0] => _0_TO_10_SECONDS           
					[1] => 3668
					[2] => 5771
					[3] => 72.59054
					[4] => 1
				)
			)
		)
	**	To =>
	**	Array(
		[0] => Array(
				[0] => 20140824           
				[1] => _0_TO_10_SECONDS           
				[2] => 3668
				[3] => 5771
				[4] => 72.59054
				[5] => 1
			)
		)
	*/
	function FlattenTrendData($data){
		$flatten = [];
		$r = 0;
		foreach ($data as $key => $rows) {

			foreach ($rows as $row) {
				# Set the first [0] as the key of the first array
				$flatten[$r][0] = $key;
	
				$i = 1;
				foreach ($row as $k => $v) {
					$flatten[$r][$i] = $v;
					
					$i ++;
				}
				$r ++;
			}
		}
		return $flatten;
	}

	function TrendData2nvd3($data,$x_key = 0, $y_key = 1) {
		$json_data = [];

		$i = 0;
		foreach ($data as $date => $row) {
			foreach ($row as $k => $v) {
				$json_data[$k]["key"] = $v[$x_key];
				$json_data[$k]["values"][] = array($i, (int)$v[$y_key]);
			}	
			$i ++;		
		}
		
		return $json_data;
	}

	function GetXticks($data){
		$x_ticks = [];
		
		foreach ($data as $date => $row) {
			$time = strtotime($date);
			if(!$time || !$this->allowDateFormat){ # If strtotime fails (false) then we can set the current value
				$x_ticks[] = $date;
			} else{
				$x_ticks[] = LogaDate($this->profile->dateFormat, $time);
			}		
		}
		return $x_ticks;
	}

	function StackedAreaChart($data, $column_header = 1, $x_key = 0, $y_key = 1, $sort_key = 0, $data_type = 'trend'){
		
		$md5_time = md5(time());
		$container_id = 'stac'.$md5_time.rand(1,1000);		

		$x_ticks = $this->GetXticks($data);

		$data = $this->TrendData2nvd3($data,$x_key, $y_key);

		$controlLabels = array("stacked" => _STACKED, "stream" => _STREAM, "expanded" => _EXPANDED);
		
		echo "<div id='$container_id' class='stackedarea' data-ticks='". json_encode($x_ticks) ."' data-chartoptions='".json_encode($this->chartoptions)."' data-chart='".json_encode($data)."' data-coldefs='".json_encode($this->columnDefinitions)."' data-controlLabels='".json_encode($controlLabels)."'>";
		echo "</div>";

		echo "<script>";
		echo "lg.StackedAreaChart('$container_id');";
		echo "</script>";

	}

	function StackedAreaChart2($data, $column_header = 1, $x_key = 0, $y_key = 1, $sort_key = 0, $data_type = 'trend'){

		$md5_time = md5(time());
		$container_id = 'stac'.$md5_time.rand(1,1000);
		
		$json_data = [];
		$i=0;
		# create one element per serie of data
		foreach ($this->columnDefinitions as $key => $value) {			
			if ($key==0) { continue; }
			if (defined($value['Label'])) {
				$label = constant($value['Label']);
			} else {
				$label = $value['Label'];
			}
			$label= strip_tags($label);
			$json_data[$i] = array("key" => $label, "values" => []);
			$i++;

		}

		# create a vals array, one for each serie of data
		$vals = [];

		$x_ticks = [];
		$i = 0;
		foreach($data as $d){

			foreach($d as $k => $v) {
				if ($k==0) { $x_ticks[] = trim($v); }

				$vals[$k-1][] = array($i, (float)$v);
			}
			$i ++;
		}
				
		# merge the vals array info the json_data array
		foreach($json_data as $k => $v) {
			$json_data[$k]['values'] = $vals[$k];
		}
		$controlLabels = array("stacked" => _STACKED, "stream" => _STREAM, "expanded" => _EXPANDED);

		echo "<div id='$container_id' class='stackedarea' data-ticks='". json_encode($x_ticks) ."' data-chartoptions='".json_encode($this->chartoptions)."' data-chart='".json_encode($json_data)."' data-coldefs='".json_encode($this->columnDefinitions)."' data-controlLabels='".json_encode($controlLabels)."'>";
		echo "</div>";

		echo "<script>";
		echo "lg.StackedAreaChart('$container_id');";
		echo "</script>";

	}

	function HorizontalMultiBar($data, $column_header = 1, $x_key = 0, $y_key = 1, $sort_key = 0, $data_type = 'trend'){
		
		$md5_time = md5(time());
		$container_id = 'stac'.$md5_time.rand(1,1000);		
		
		
		echo "<div id='$container_id' class='HorizontalMultiBar' data-ticks='". json_encode($x_ticks) ."' data-chartoptions='".json_encode($this->chartoptions)."' data-chart='".json_encode($data)."' data-coldefs='".json_encode($this->columnDefinitions)."'>";
		echo "</div>";

		echo "<script>";
		echo "lg.HorizontalMultiBar('$container_id');";
		echo "</script>";

	}

	
	function ConvertDataforCollapseTable($d){
		global $cnames;
		$data = [];
		
		foreach ($d as $k => $row) {
			// level keys
			$l1 = $row[ $this->collapsekeys[0] ];

			$data[$l1][0] = "level1";
			//add the label
			if ($this->columnDefinitions[0]["Label"]=="_COUNTRY") {
				if (isset($cnames[$row[ $this->collapsekeys[0] ]]) ) {
					$row[ $this->collapsekeys[0] ] = $cnames[$row[ $this->collapsekeys[0] ]];
				} else {
					$row[ $this->collapsekeys[0] ] = _UNKOWN_LOCATIONS;
				}
			}
			$data[$l1][1] = $row[ $this->collapsekeys[0] ];
			// now add metrics
			$m = 2;
			foreach ($this->sumkeys as $sk => $sval) {
				if (!isset($data[$l1][$m])) { $data[$l1][$m]=0; }
				$data[$l1][$m] += $row[ $this->sumkeys[$sk] ];
				// they keys have changed in this new array, we need to map the old cols to the new ones so we know which one to sort on later
				$this->keymap[$sk] = $m;
				$m++;
			}
			
			//now add the second level
			$l2 = $l1.$row[ $this->collapsekeys[1] ];
			$data[$l1][$m][$l2][0] = "level2";
			$data[$l1][$m][$l2][1] = $row[ $this->collapsekeys[1] ];
			$lm = 2;
			foreach ($this->sumkeys as $sk => $sval) {
				if (!isset($data[$l1][$m][$l2][$lm])) { $data[$l1][$m][$l2][$lm]=0; }
				$data[$l1][$m][$l2][$lm] += $row[ $this->sumkeys[$sk] ];
				$lm++;
			}

			if (count($this->collapsekeys) > 2) {
				//now add the third level
				$l3 = $l2.$row[ $this->collapsekeys[2] ];
				$data[$l1][$m][$l2][$lm][$l3][0] = "level3";
				$data[$l1][$m][$l2][$lm][$l3][1] = $row[ $this->collapsekeys[2] ];
				$llm = 2;
				foreach ($this->sumkeys as $sk => $sval) {
					if (!isset($data[$l1][$m][$l2][$lm][$l3][$llm])) { $data[$l1][$m][$l2][$lm][$l3][$llm]=0; }
					$data[$l1][$m][$l2][$lm][$l3][$llm] += $row[ $this->sumkeys[$sk] ];
					$llm++;
				}
			}

			if (count($this->collapsekeys) > 3) {
				//now add the fourth level
				$l4 = $l3.$row[ $this->collapsekeys[3] ];
				$data[$l1][$m][$l2][$lm][$l3][$llm][$l4][0] = "level4";
				$data[$l1][$m][$l2][$lm][$l3][$llm][$l4][1] = $row[ $this->collapsekeys[3] ];
				$lllm = 2;
				foreach ($this->sumkeys as $sk => $sval) {
					if (!isset($data[$l1][$m][$l2][$lm][$l3][$llm][$l4][$lllm])) { $data[$l1][$m][$l2][$lm][$l3][$llm][$l4][$lllm]=0; }
					$data[$l1][$m][$l2][$lm][$l3][$llm][$l4][$lllm] += $row[ $this->sumkeys[$sk] ];
					$lllm++;
				}
			}

			if (count($this->collapsekeys) > 4) {
				//now add the fifth level
				$l5 = $l4.$row[ $this->collapsekeys[4] ];	
				$data[$l1][$m][$l2][$lm][$l3][$llm][$l4][$lllm][$l5][0] = "level5";
				$data[$l1][$m][$l2][$lm][$l3][$llm][$l4][$lllm][$l5][1] = $row[ $this->collapsekeys[4] ];
				$llllm = 2;
				foreach ($this->sumkeys as $sk => $sval) {
					if (!isset($data[$l1][$m][$l2][$lm][$l3][$llm][$l4][$lllm][$l5][$llllm])) { $data[$l1][$m][$l2][$lm][$l3][$llm][$l4][$lllm][$l5][$llllm]=0; }
					$data[$l1][$m][$l2][$lm][$l3][$llm][$l4][$lllm][$l5][$llllm] += $row[ $this->sumkeys[$sk] ];
					$llllm++;
				}
			}
			
		}
		//dump($data);
		//exit('hier');
		// now sort all 3 levels
		$data = DataSort($data,$this->keymap[0]);
		foreach ($data as $key => $level) {
			foreach ($level as $k => $v) {
				if (is_array($v)) {
					$data[$key][$k] = DataSort($v,$this->keymap[0]);
					foreach ($v as $lk => $level2) {
						//dump($level2);
						foreach ($level2 as $k2 => $v2) {
							if (is_array($v2)) {
								$data[$key][$k][$lk][$k2] = DataSort($v2,$this->keymap[0]);
								foreach ($v2 as $llk => $level3) {
									//dump($level3);
									foreach ($level3 as $k3 => $v3) {
										if (is_array($v3)) {
											$data[$key][$k][$lk][$k2][$llk][$k3] = DataSort($v3,$this->keymap[0]);

											foreach ($v3 as $lllk => $level4) {
												//dump($level3);
												foreach ($level4 as $k4 => $v4) {
													if (is_array($v4)) {
														$data[$key][$k][$lk][$k2][$llk][$k3][$lllk][$k4] = DataSort($v4,$this->keymap[0]);
													}
												}
											}

										}
									}
								}
							}
						}
					}
				}
			}
			//dump($level);
			//$data[$key] = DataSort($level,$this->keymap[0]);
		}
		//dump($data)	;
		$data = array_slice($data, 0, $this->report_limit);	
		//dump($data)	;
		return $data;
	}

	function CollapseTable($data, $collapsekeys, $sumkeys) {

		// lets make some nice graphcolors
		$this->graphcolors = [];
		$this->graphcolors["level1-2"] = "#73B2D4";
		$this->graphcolors["level1-3"] = "#FFB370";

		$this->graphcolors["level2-2"] = "#92C3DD";
		$this->graphcolors["level2-3"] = "#FFBE85";

		$this->graphcolors["level3-2"] = "#B1D4E7";
		$this->graphcolors["level3-3"] = "#FFC999";

		$this->graphcolors["level4-2"] = "#D0E5F1";
		$this->graphcolors["level4-3"] = "#FFD3AD";

		$this->graphcolors["level5-2"] = "#E0EEF5";
		$this->graphcolors["level5-3"] = "#FFDEC2";

		// $this->graphcolors["level1-2"] = $this->graphcolors["level1-3"];
		// $this->graphcolors["level2-2"] = $this->graphcolors["level2-3"];
		// $this->graphcolors["level3-2"] = $this->graphcolors["level3-3"];
		// $this->graphcolors["level4-2"] = $this->graphcolors["level4-3"];
		// $this->graphcolors["level5-2"] = $this->graphcolors["level5-3"];

		$this->collapsekeys = $collapsekeys;
		$this->sumkeys = $sumkeys;
		
		$data = $this->ConvertDataforCollapseTable($data);

		// we need to hide cols that are collapsed into the first col
		foreach($this->collapsekeys as $k => $v) {
			// skip the first one
			if ($k==0) { continue; }
			// hide the rest
			$this->columnDefinitions[$v]["display"] = false;
		}

		$table_id = md5('Collapse'.time().rand(0,10000));	
				

		echo "<table id=\"{$table_id}\" class=\"collapseTable table report_table\" cellspacing=\"0\" cellpadding=\"3\" border=\"0\" style=\"width: 100%;\">";
			echo "<thead>";	
				echo "<tr class='tabletotalcolor' data-level=\"header\" class=\"header\">";
				foreach ($this->columnDefinitions as $key => $definition) {
					$colname = $definition["Label"];
					if(defined($colname)){
						$colname = constant($colname);
					}
					if(!isset($definition['display']) || $definition['display'] == true){
						echo "<th>$colname</th>";
					}
				}
				echo "</tr>";
			echo "</thead>";


			echo "<tbody>";

			// first make some totals			
			foreach ($data as $c => $level1) {
				$i=0;
				foreach ($this->keymap as $sval) {
					if (!isset($totals[$i])) { $totals[$i] = 0; }
					if (!isset($maxval[$i])) { $maxval[$i] = 0; }

					$totals[$i] += $level1[$sval];
					if ($maxval[$i] < $level1[$sval]) {
						$maxval[$i] = $level1[$sval];
					}
					$i++;	
				}				
			}			
			// now display the result
			foreach ($data as $c => $level1) {

				$this->CollapseTablePrintBlock($level1, $totals, $maxval, 1);
				
			}
			echo "</tbody>";

			echo "<tfoot>";
				echo "<tr class='tabletotalcolor'><td><b>". _TOTAL ."</b></td>";
				foreach ($totals as $total) {
					echo "<td><b>".number_format($total)."</b></td>";
				}
				echo "</tr>";
			echo "</tfoot>";

		echo "</table>";
		?>
		<script type="text/javascript">
		$("#<?php echo $table_id; ?> tr.level1").on("click", function(){

			$(this).nextUntil('tr.level1').hide();

			if( $(this).hasClass("expanded") ){
				$(this).removeClass("expanded");
				$(this).find(".fa").removeClass("fa-folder-open");
				$(this).find(".fa").addClass("fa-folder-o");
			} else {
				$(this).addClass("expanded");
				$(this).nextUntil('tr.level1').not(".level3,.level4,.level5").toggle();
				$(this).find(".fa").removeClass("fa-folder-o");
				$(this).find(".fa").addClass("fa-folder-open");
			}
		});

		$("#<?php echo $table_id; ?> tr.level2").on("click", function(){
			if( $(this).hasClass("expanded") ){
				$(this).removeClass("expanded");
				$(this).find(".fa").removeClass("fa-folder-open");
				$(this).find(".fa").addClass("fa-folder-o");
			} else {
				$(this).addClass("expanded");				
				$(this).find(".fa").removeClass("fa-folder-o");
				$(this).find(".fa").addClass("fa-folder-open");
			}

			$(this).nextUntil('tr.level2').not('tr.level1,.level4,.level5').toggle();
		});

		$("#<?php echo $table_id; ?> tr.level3").on("click", function(){
			if( $(this).hasClass("expanded") ){
				$(this).removeClass("expanded");
				$(this).find(".fa").removeClass("fa-folder-open");
				$(this).find(".fa").addClass("fa-folder-o");
			} else {
				$(this).addClass("expanded");				
				$(this).find(".fa").removeClass("fa-folder-o");
				$(this).find(".fa").addClass("fa-folder-open");
			}

			$(this).nextUntil('tr.level3').not('.level1,.level2,.level5').toggle();
		});

		$("#<?php echo $table_id; ?> tr.level4").on("click", function(){
			if( $(this).hasClass("expanded") ){
				$(this).removeClass("expanded");
				$(this).find(".fa").removeClass("fa-folder-open");
				$(this).find(".fa").addClass("fa-folder-o");
			} else {
				$(this).addClass("expanded");				
				$(this).find(".fa").removeClass("fa-folder-o");
				$(this).find(".fa").addClass("fa-folder-open");
			}

			$(this).nextUntil('tr.level4').not('tr.level1,.level2').toggle();
		});
		</script>
		<?php
	}

	function CollapseTablePrintBlock($level, $totals, $maxval, $nlevel) {		

		if (isset($this->class[$nlevel])) { 
			$class = $this->class[$nlevel]; 
		} else {
			if (isset($this->class[$nlevel-1])) { 
				$class = $this->class[$nlevel-1];
			} else {
				$class = "odd";
			}
		}		
		// calculate width and percentage of the barchart
		foreach ($this->keymap as $sk => $sval) {					
			if ($totals[$sk]>0) {
				$perc[$sk] = ($level[$sval]/$totals[$sk])*100;
				$percwidth[$sk] = ($level[$sval]/$maxval[$sk])*100;
			} else {
				$perc[$sk] = 0;
				$percwidth[$sk] = 0;
			}						
		}

		$show ="display:none";
		if ($nlevel ==1 ) { $show =""; }
		if($class == "odd"){$class = "gray_row even";} else {$class = "odd";}
		$this->class[$nlevel]=$class;	
		echo "<tr class='{$level[0]} {$class}' style='$show'>";

		foreach ($level as $k => $item) {
			if ($k==0) { continue; }

			if ($k==1) {
				$expand_icon = "<i class='fa fa-folder-o'></i>";
				if ($nlevel == count($this->collapsekeys) ) { $expand_icon="<i class='fa fa-dot-circle-o'></i>"; }
				$tl ="";
				if ($nlevel==1) {
					if (!empty($this->trendlink) && $level[1]!=='Unknown Locations') {
						$tl = str_replace("%country", $level[1], $this->trendlink);
					} 
				}
				echo "<td>{$tl}{$expand_icon} {$item}</td>";
			} else if (in_array($k, $this->keymap)) {
				$sk = array_search($k, $this->keymap);
				$colorkey = $level[0]."-".$k;
				echo "<td style='white-space:nowrap;'><div sort=\"{$item}\" class='graphborder' style='width:{$percwidth[$sk]}%;background-color:{$this->graphcolors[$colorkey]};' title='".number_format($item)."'>".number_format($perc[$sk],1)."%</div></td>";
			} else if (is_array($item)) {
				//next level				
				echo "</tr>";				
				// first make some totals			
				foreach ($item as $ik => $sublevel) {
					$i=0;
					foreach ($this->keymap as $sval) {
						if (!isset($subtotals[$i])) { $subtotals[$i] = 0; }
						if (!isset($submaxval[$i])) { $submaxval[$i] = 0; }

						$subtotals[$i] += $sublevel[$sval];
						if ($submaxval[$i] < $sublevel[$sval]) {
							$submaxval[$i] = $sublevel[$sval];
						}
						$i++;	
					}				
				}

				foreach ($item as $ik => $sublevel) {
					$this->CollapseTablePrintBlock($sublevel, $subtotals, $submaxval, $nlevel+1);
				}				
			}
		}
	}

	function Sunburst($data) {
		//dump($data);
		global $lang;
		$data = $this->ConvertDataForSunburst($data);
		//dump($data);
		$md5_time = md5(time());
		$container_id = 'burst'.$md5_time.rand(1,1000);		
		if (!isset($this->sunburstcolors)) {
			$this->sunburstcolors = array("d3cat" => "d3.scale.category20c().range()");
		}
		
		echo "<div id='$container_id' class='Sunburst' data-chartoptions='".json_encode($this->chartoptions)."' data-chart='".str_replace("'", "&#39;", json_encode($data))."' data-coldefs='".json_encode($this->columnDefinitions)."' data-colors='".json_encode($this->sunburstcolors)."'>";
		echo "</div>";

		echo "<script>";
		echo "lg.Sunburst('$container_id');";
		echo "</script>";

		if ($lang=="english") {
			echoNotice("Interactive chart:<ul><li>Click a slice to zoom in</li><li>Click the center to zoom out</li><li>Hover over a slice to see values</li></ul>");
		}

	}

	function ConvertDataForSunburst($d) {


		$d = $this->ConvertDataforCollapseTable($d);

		$data[0]['name'] = "Universe";		
		foreach($d as $val) {
			$data[0]['children'][] = $this->SunburstLevel($val, 0);
		}
		return $data;
	}

	function SunburstLevel($val, $l) {
		
		$data = [];
		$data['name'] = $val[1];
		$next_level = array_pop($val);			

		if (is_array($next_level)) {
			foreach($next_level as $next_val) {
				$data['children'][] = $this->SunburstLevel($next_val , $l++ );
			}
		} else {
			if (!isset($val[2])) {
				//we popped of our value, restore it
				$val[2] = $next_level;
			}
			$data['size'] = $val[2];			
		}		
		return $data;
	}

}

	
?>