

/** <-------------------------------------------------------------------------------- Data --------------------------------------------------------------------------------> **/
var lgTutorial = {};
lgTutorial.tutorials = {};
lgTutorial.showtime;
lgTutorial.step = 0;
lgTutorial.total_steps = 0;
lgTutorial.tutorial_settings = {"Name" : "" ,"Intro" : ""	,"End" : "" };
lgTutorial.tutorial_config = [];
lgTutorial.default_height = "260px";
lgTutorial.default_min_height = "70px";

lgTutorial.settings = function(intro){
	lgTutorial.tutorial_settings = intro;
}
lgTutorial.init = function(config){
	lgTutorial.step = 0;
	lgTutorial.tutorial_config = config;
	lgTutorial.total_steps = config.length;

}

lgTutorial.removeTooltip = function(){
	$('#tour_tooltip').remove();
}

lgTutorial.closeTutorial = function(){
	clearTimeout(lgTutorial.showtime);

	if($("#tutorialbox input[name='nevershowagain']").is(":checked")){
		var d = new Date();
		d.setTime(d.getTime() + (1365*24*60*60*1000));
		document.cookie = "hideTutorial=1; expires="+ d.toGMTString();
	}

	$('#tutorialbox').remove();
	$('#tour_overlay').remove();
	$('#tour_tooltip').remove();
	$('.tutorial_controls').remove();
}

lgTutorial.EndTutorial = function(){
	lgTutorial.removeTooltip();
	lgTutorial.closeTutorial();
}

lgTutorial.nextStep = function(){
	if(lgTutorial.step >= lgTutorial.total_steps){
		//if last step then end tour
		lgTutorial.EndTutorial();
		return false;
	}
	lgTutorial.step ++;
	lgTutorial.showTooltip();
}

lgTutorial.showTooltip = function(){
	//remove current tooltip
	lgTutorial.removeTooltip();

	var step_config		= lgTutorial.tutorial_config[lgTutorial.step-1];

	//console.log(step_config);
	// Check if the target is undefined. if so we want to just wait it out
	if(step_config.target == undefined){
		clearTimeout(lgTutorial.showtime);
		lgTutorial.showtime = setTimeout(function(){
			lgTutorial.nextStep();
		},step_config.time);
		return false;
	}



	var $elem			= $(step_config.target);

	lgTutorial.showtime	= setTimeout(function(){
		// check for a callback call before showing the next tooltip
		if (step_config.callback_after != undefined && typeof(step_config.callback_after) === "function") {
			step_config.callback_after();
		}
		lgTutorial.nextStep();
	},step_config.time);

	var $tooltip		= $('<div>',{
		id			: 'tour_tooltip',
		className 	: 'tooltip',
		html		: '<div><p>'+step_config.text+'</p></div><span class="tooltip_arrow"></span>'
	}).css({
		'display'			: 'none',
	});

	// check for a callback call before showing the tooltip
	if (step_config.callback_before != undefined && typeof(step_config.callback_before) === "function") {
		step_config.callback_before();
	}

	//position the tooltip correctly:

	//the css properties the tooltip should have
	var properties		= {};

	var tip_position 	= step_config.position;

	//append the tooltip but hide it
	$('BODY').prepend($tooltip);


	// do we need to adjust the box a bit left or right?
	var adjust_left = 0;
	if(step_config.adjust_left != undefined){
		adjust_left = step_config.adjust_left;
	}

	// do we need to adjust the box a bit up or down?
	var adjust_top = 0;
	if(step_config.adjust_top != undefined){
		adjust_top = step_config.adjust_top;
	}


	//get some info of the element
	var e_w				= $elem.outerWidth();
	var e_h				= $elem.outerHeight();
	var e_l				= $elem.offset().left + parseInt(adjust_left);
	var e_t				= $elem.offset().top + parseInt(adjust_top);


	switch(tip_position){
		case 'TL'	:
			properties = {
				'left'	: e_l,
				'top'	: e_t + e_h + 8 + 'px'
			};
			$tooltip.find('span.tooltip_arrow').addClass('tooltip_arrow_TL');
			break;
		case 'TR'	:
			properties = {
				'left'	: e_l + e_w - $tooltip.width() + 'px',
				'top'	: e_t + e_h + 'px'
			};
			$tooltip.find('span.tooltip_arrow').addClass('tooltip_arrow_TR');
			break;
		case 'BL'	:
			properties = {
				'left'	: e_l + 'px',
				'top'	: e_t - $tooltip.height() + 'px'
			};
			$tooltip.find('span.tooltip_arrow').addClass('tooltip_arrow_BL');
			break;
		case 'BR'	:
			properties = {
				'left'	: e_l + e_w - $tooltip.width() + 'px',
				'top'	: e_t - $tooltip.height() + 'px'
			};
			$tooltip.find('span.tooltip_arrow').addClass('tooltip_arrow_BR');
			break;
		case 'CL'	:
			properties = {
				'left'	: '45%',
				'top'	: '200px'
			};
			//$tooltip.find('span.tooltip_arrow').addClass('tooltip_arrow_L');
			break;
		case 'LT'	:
			properties = {
				'left'	: e_l + e_w + 'px',
				'top'	: e_t + 'px'
			};
			$tooltip.find('span.tooltip_arrow').addClass('tooltip_arrow_LT');
			break;
		case 'LB'	:
			properties = {
				'left'	: e_l + e_w + 'px',
				'top'	: e_t + e_h - $tooltip.height() + 'px'
			};
			$tooltip.find('span.tooltip_arrow').addClass('tooltip_arrow_LB');
			break;
		case 'RT'	:
			properties = {
				'left'	: e_l - $tooltip.width() + 'px',
				'top'	: e_t + 'px'
			};
			$tooltip.find('span.tooltip_arrow').addClass('tooltip_arrow_RT');
			break;
		case 'RB'	:
			properties = {
				'left'	: e_l - $tooltip.width() + 'px',
				'top'	: e_t + e_h - $tooltip.height() + 'px'
			};
			$tooltip.find('span.tooltip_arrow').addClass('tooltip_arrow_RB');
			break;
		case 'T'	:
			properties = {
				'left'	: e_l + e_w/2 - $tooltip.width()/2 + 'px',
				'top'	: e_t + e_h + 8 + 'px'
			};
			$tooltip.find('span.tooltip_arrow').addClass('tooltip_arrow_T');
			break;
		case 'R'	:
			properties = {
				'left'	: e_l - $tooltip.width() + 'px',
				'top'	: e_t + e_h/2 - $tooltip.height()/2 + 'px'
			};
			$tooltip.find('span.tooltip_arrow').addClass('tooltip_arrow_R');
			break;
		case 'B'	:
			properties = {
				'left'	: e_l + e_w/2 - $tooltip.width()/2 + 'px',
				'top'	: e_t - $tooltip.height() + 'px'
			};
			$tooltip.find('span.tooltip_arrow').addClass('tooltip_arrow_B');
			break;
		case 'L'	:
			properties = {
				'left'	: e_l + e_w  + 'px',
				'top'	: e_t + e_h/2 - $tooltip.height()/2 + 'px'
			};
			$tooltip.find('span.tooltip_arrow').addClass('tooltip_arrow_L');
			break;
	}

	/*
	if the element is not in the viewport
	we scroll to it before displaying the tooltip
	 */
	var w_t	= $(window).scrollTop();
	var w_b = $(window).scrollTop() + $(window).height();
	//get the boundaries of the element + tooltip
	var b_t = parseFloat(properties.top,10);

	if(e_t < b_t)
		b_t = e_t;

	var b_b = parseFloat(properties.top,10) + $tooltip.height();
	if((e_t + e_h) > b_b)
		b_b = e_t + e_h;


	if((b_t < w_t || b_t > w_b) || (b_b < w_t || b_b > w_b)){
		$('html, body').stop()
			.animate({scrollTop: b_t}, 500, 'easeInOutExpo', function(){
				//need to reset the timeout because of the animation delay
				clearTimeout(lgTutorial.showtime);
				lgTutorial.showtime = setTimeout(function(){
					lgTutorial.nextStep();

				},step_config.time);


				//show the new tooltip
				$tooltip.css(properties).show();
			});
	}
	else
		//show the new tooltip
		$tooltip.css(properties).show();
}

lgTutorial.showOverlay = function(){
	var $overlay	= '<div id="tour_overlay" class="overlay"></div>'+
		'<div class="tutorial_controls"><span class="tutorial_buttons">&nbsp;&nbsp;&nbsp;&nbsp;<a class="btn btn-lg btn-default prevstep" href="#"><i class="fa fa-step-backward"></i></a>'+
		' <a class="btn btn-lg btn-default stop" href="#" style="display:none"><i class="fa fa-stop"></i></a>'+
		' <a class="btn btn-lg btn-default pause" href="#"><i class="fa fa-pause"></i></a>'+
		' <a class="btn btn-lg btn-default nextstep" href="#"><i class="fa fa-step-forward"></i></a>'+
		'&nbsp;&nbsp;&nbsp;&nbsp;</span></div>';
	$('BODY').prepend($overlay);

	$(".tutorial_controls .nextstep").on("click", function(e) {
		e.stopPropagation();
		e.preventDefault();
		//$(".tutorial_controls .pause i").switchClass("fa-stop","fa-pause");
		$(".tutorial_controls .pause").show();
		$(".tutorial_controls .stop").hide();

		clearTimeout(lgTutorial.showtime);
		var step_config		= lgTutorial.tutorial_config[lgTutorial.step-1];
		if (step_config.callback_after != undefined && typeof(step_config.callback_after) === "function") {
			step_config.callback_after();
		}
		lgTutorial.nextStep();
	});
	$(".tutorial_controls .prevstep").on("click", function(e) {
		e.stopPropagation();
		e.preventDefault();
		//$(".tutorial_controls .pause i").switchClass("fa-stop","fa-pause");
		$(".tutorial_controls .pause").show();
		$(".tutorial_controls .stop").hide();
		clearTimeout(lgTutorial.showtime);
		lgTutorial.step= lgTutorial.step-2;
		if (lgTutorial.step < 0) {
			lgTutorial.step =0;
		} else {
			var step_config		= lgTutorial.tutorial_config[lgTutorial.step-1];
			if (step_config.callback_after != undefined && typeof(step_config.callback_after) === "function") {
				step_config.callback_after();
			}
		}
		lgTutorial.nextStep();
	});
	$(".tutorial_controls .pause").on("click", function(e) {
		e.stopPropagation();
		e.preventDefault();
		clearTimeout(lgTutorial.showtime);
		$(".tutorial_controls .pause").hide();
		$(".tutorial_controls .stop").show();

	});
	$(".tutorial_controls .stop").on("click", function(e) {
		lgTutorial.EndTutorial();
	});
}

lgTutorial.start = function(){
	$('#tutorialPopUp').modal('hide');
	// extra call because for example: the use might have closed or opened the report panel
	lgTutorial.showOverlay();
	lgTutorial.nextStep();

}


var plots = [];

// get the available profiles by ajax request
function GetAvailableProfiles(request,callback){
	var report_id ="";
	if (request['report_id']!==undefined) {
		report_id = request['report_id'];
	}

	$.ajax({
		type: 'POST',
		url: 'includes/available_profiles.php',
		data:{
			search : request['term'],
			multi : request['multi'],
		},
		success: function(result) {
			// check if it is an array
			if(result.charAt(0) == '['){
				if (report_id!="") {
					$("#"+ report_id +" .report-options [name='profileselector']").parents(".form-group").first().removeClass('has-error');
					$("#"+ report_id +" .profiles-box .search").parents(".form-group").first().removeClass('has-error');
				}
				callback(JSON.parse(result));
			} else {
				if (report_id!="") {
					$("#"+ report_id +" .report-options [name='profileselector']").parents(".form-group").first().addClass('has-error');
					$("#"+ report_id +" .profiles-box .search").parents(".form-group").first().addClass('has-error');
				}
				console.log(result);
			}
		}
	});
}

// get the available countries
function GetAvailableCountries(request,callback){
	$.ajax({
		type: 'POST',
		url: 'includes/available_countries.php',
		data:{
			search : request['term']
		},
		success: function(result) {
			// check if it is an array
			if(result.charAt(0) == '['){
				callback(JSON.parse(result));
			} else {
				console.log(result);
			}
		}
	});
}

function switchProfile(obj) {

	var newProfile = obj.attr("value");
	var report_id = obj.parents(".box").first().attr("id");

	$(".report-options").modal('hide');

	report.LoadReport(report_id,{ profileselector : newProfile },undefined, function(result){
		/* set the content */
		$("#"+ report_id +" .box-body").html(result);

		/* add option handling */
		report.HandleReportOptions(report_id);
		$("#options-" + report_id ).modal('show');

	});

}

// Set a cookie whenever a category gets hidden or shown
function setCategoryCookie(label, hidden) {
	var currentTime = new Date();
	var month = currentTime.getMonth();
	var day = currentTime.getDate();
	var year = currentTime.getFullYear();
	if(month < 11) {
		month = month + 1;
	} else {
		month = 0;
		year = year + 1;
	}
	var expire_date = new Date (year, month, day);

	document.cookie = label + "=" + hidden + ";expires=" + expire_date.toGMTString();
}



function attachReportEvents (reportID) {
	/**
	 This function attaches any events to newly created or reloaded reports.

	 Arguments:
	 - reportID
	 The report that's getting its events attached.
	 **/

	$("#report-grid").sortable({
		items: '.dialog',
		handle: '.dialog-header',
		connectWith: '.grid',
		opacity: 0.3, // opacity of the element while draging
		placeholder: 'ui-state-highlight',
		helper: 'clone',
		forcePlaceholderSize: true,
		forceHelperSize: true,
		tolerance: 'pointer',
		distance: 10,
		start: function(event, ui) {
			var c = 0;
			$("#report-grid").find(".grid").each(function() {
				$(this).css('height', $("#report-grid .grid:eq(" + getLargestGrid() + ")").height() + c);
				c++;
			});

			current_drag = ui.item.index();
		},
		stop: function(event, ui) {
			var dialogID = ui.item.attr('id');

			if(ui.item.index() > current_drag) {
				var neighbour = ui.item.prev().attr('id');
				var new_icon = $(".workbar .workicons .workbar_grid .icon[rel=" + dialogID + "]").clone();
				$(".workbar .workicons .workbar_grid .icon[rel=" + dialogID + "]").remove();
				new_icon.insertAfter(".workbar .workicons .workbar_grid:eq(" + ui.item.parent().index() + ") .icon[rel='" + neighbour + "']");
			} else if(ui.item.index() <= current_drag) {
				var neighbour = ui.item.next().attr('id');
				var new_icon = $(".workbar .workicons .workbar_grid .icon[rel=" + dialogID + "]").clone();
				$(".workbar .workicons .workbar_grid .icon[rel=" + dialogID + "]").remove();
				if(neighbour != undefined) {
					new_icon.insertBefore(".workbar .workicons .workbar_grid:eq(" + ui.item.parent().index() + ") .icon[rel='" + neighbour + "']");
				} else {
					if(ui.item.index() > 0) {
						$(".workbar .workicons .workbar_grid:eq(" + ui.item.parent().index() + ")").append(new_icon);
					} else {
						$(".workbar .workicons .workbar_grid:eq(" + ui.item.parent().index() + ")").prepend(new_icon);
					}
				}
			}

		}
	});

	$("#" + reportID + " .dialog-header").disableSelection();
}



function prepareURL (reportURL, reportURLOptions) {
	/**
	 The name says it all; We'll prepare an URL for loading a report.

	 Arguments:
	 - reportURL
	 The URL that we're about to prepare.
	 - reportURLOptions
	 The options of the report whose URL we're about to prepare.
	 **/

	conf_name;
	var URLoptions = "";
	var URLparametersSet = false;

	if(reportURL.indexOf("?") > -1) {
		URLparametersSet = true;
	}

	if(reportURLOptions.minimumDate != undefined) {
		if(URLparametersSet == false) {
			URLoptions += "?";
		} else {
			URLoptions += "&";
		}
		URLoptions += "minimumDate=" + reportURLOptions.minimumDate;
		URLparametersSet = true;
	}

	if(reportURLOptions.maximumDate != undefined) {
		if(URLparametersSet == false) {
			URLoptions += "?";
		} else {
			URLoptions += "&";
		}
		URLoptions += "maximumDate=" + reportURLOptions.maximumDate;
		URLparametersSet = true;
	}

	for(var option in reportURLOptions) {
		if(option == "minimumDate" || option == "maximumDate") { continue; }

		if(URLparametersSet == false) {
			URLoptions += "?";
		} else {
			URLoptions += "&";
		}

		URLoptions += option + "=" + reportURLOptions[option];
		URLparametersSet = true;
	}

	reportURL = reportURL + URLoptions;

	if(URLparametersSet == false) {
		reportURL += "?";
	} else {
		reportURL += "&";
	}
	reportURL += "conf=" + conf_name;

	return completeURL = reportURL;
}



function getUniqueReportID (reportName) {
	/**
	 This function will create a unique ID for a report, based on the report's name.

	 Arguments:
	 - reportName
	 The name of the report; we'll use this name for the unique ID.
	 **/

	var id = replaceAll("/","",reportName);
	var tstamp = new Date();
	var chosenID = id + "--" + tstamp.getTime();

	return chosenID;
}



function applyOptionsToReport (reportID, currentReportOptions) {
	/**
	 We'll apply the options to the report, saving the options in a JQuery Data Object.

	 Arguments:
	 - reportID
	 The ID of the report.
	 - currentReportOptions
	 The options of the report.
	 **/

	$("#" + reportID).attr("options", JSON.stringify(currentReportOptions));
}


function doAjaxRequest (elementSelector, loadURL, callback_func, error_func, isAsynchronous) {
	/**
	 The name says it all; We're going to do an AJAX Request.

	 Arguments:
	 - elementSelector
	 A JQuery selector of the container of the result of the request. This can be anything - as long as the element exists.
	 - loadURL
	 The URL which we're going to load through AJAX.
	 - callback_func [optional]
	 The function we're going to call once the requests succeeds.
	 - error_func [optional]
	 The function we're going to call, should the request fail.
	 - isAsynchronous [optional]
	 A boolean containing whether we'll do an asynchronous request, or a synchronous request.
	 By default we'll do a synchronous request.
	 **/

	if(callback_func == undefined) {
		callback_func = function() {};
	}
	if(error_func == undefined) {
		error_func = function() {};
	}
	if(isAsynchronous == undefined) {
		isAsynchronous = false;
	}

	ajax_requests[elementSelector] = $.ajax({
		url: loadURL,
		async: isAsynchronous,
		success: function(result, status) {
			try {
				$(elementSelector).html(result);
			} catch(err) {
				try {
					console.log(err);
				} catch(err) {
					/* hoer ie */
				}
			}
			callback_func();

			debugToConsole("Succeeded AJAX Request from " + loadURL + " to " + elementSelector);
		}, error: function(result, status) {
			error_func();

			debugToConsole("Failed AJAX Request from " + loadURL + " to " + elementSelector);
		}
	});
}

function sortObject(o) {
	var sorted = {},
		key, a = [];

	for (key in o) {
		if (o.hasOwnProperty(key)) {
			a.push(key);
		}
	}

	a.sort();

	for (key = 0; key < a.length; key++) {
		sorted[a[key]] = o[a[key]];
	}
	return sorted;
}


/** <-------------------------------------------------------------------------------- Actions --------------------------------------------------------------------------------> **/

function getStoreReports (received_reports) {

	for(var report_key in received_reports) {
		if(received_reports[report_key]['package'] != undefined) {
			var bundle = received_reports[report_key]['package'].split(",");

			for(var bundle_key in bundle) {
				if($.inArray(bundle[bundle_key], class_reports) >= 0) {
					delete received_reports[report_key];
				}
			}
		}
	}
	shop_reports = received_reports;
	createStoreList();
	fixAfterWindowResize();
}

function getRandomStoreReport () {
	var tmp_shop_reports = [];
	for(var shop_report in shop_reports) {
		tmp_shop_reports.push(shop_reports[shop_report]);
	}

	var randomReport = tmp_shop_reports[Math.floor(Math.random() * parseFloat(tmp_shop_reports.length))];

	if($(".reportstore_icon[rel='" + randomReport['product_id'] + "']").length >= 1) {
		randomReport = getRandomStoreReport();
	}

	return randomReport;
}

function openDialog (eventTarget) {
	/**
	 This function opens a JQuery Dialog and fills it with the eventTarget's HREF attribute.

	 Arguments:
	 - eventTarget
	 The element which caused this function to be called.
	 **/

	if($('#' + eventTarget.attr('id') + 'Dialog').length == 0) { // If the dialog exists, we'll bring it to the front, if it doesn't exist, we'll create it.
		var addClass = '';
		if(eventTarget.attr('id') == 'notebook') { // If we want to open the notes, we'll add a class, so the dialog has its own styling.
			addClass = 'notebook';
		}
		$('#content').prepend('<div id=\'' + eventTarget.attr('id') + 'Dialog' + '\'></div>');
		var dialogID = eventTarget.attr('id') + 'Dialog';
		var dialog = $('#' + dialogID);

		if(eventTarget.attr('title') == undefined || eventTarget.attr('title') == "") { // Either the name or the title of the selected element will be used as the header of the new dialog.
			var dialogtitle = eventTarget.attr('name');
		} else {
			var dialogtitle = eventTarget.attr('title');
		}

		var url = eventTarget.attr('href');
		$.ajax({
			url: url,
			success: function(result, status) {
				dialog.html(result); // We'll fill the dialog with the contents of the AJAX request.
				dialog.dialog({ close: function() { $(this).remove() }, width: 'auto', height: 'auto', position: ['center','middle'], title: dialogtitle, dialogClass: addClass });
			},
			error: function(result, status) {
				dialog.html(Error("this Window"));
				dialog.dialog({ close: function() { $(this).remove() }, width: 'auto', height: 'auto', position: ['center','middle'], title: dialogtitle, dialogClass: addClass });
			}
		});
	} else {
		bringToFront($('#' + eventTarget.attr('id') + 'Dialog'));
	}
	return false;
}

function optionsToURL(url, options) {
	var url_params = {
		conf: conf_name
	};
	var url_parts = url.split("?")[1];

	if(url_parts != undefined) {
		url_parts = url_parts.split("&");

		for(var c in url_parts) {
			var tmp_url_parts = url_parts[c].split("=");
			url_params[tmp_url_parts[0]] = tmp_url_parts[1];
		}
	}

	for(var option_key in options) {
		if(option_key == 'minimumDate' || option_key == 'maximumDate') {
			options[option_key] = options[option_key];
		}

		if(option_key == 'from' || option_key == 'to') {
			continue;
		}

		url_params[option_key] = addslashes(options[option_key]);
	}

	var c = 0;
	var parsed_url = url.split("?")[0] + "?";
	for(var param_key in url_params) {
		if(c > 0) { parsed_url += "&"; }
		parsed_url += param_key + "=" + url_params[param_key];
		c++;
	}
	return parsed_url;
}

function urlToOptions(reporturl) {
	var options = {};
	var tmp_url = reporturl.split("?");
	tmp_url = tmp_url[1];
	tmp_url = tmp_url.split("&");

	for(var x in tmp_url) {
		options[tmp_url[x].split("=")[0]] = tmp_url[x].split("=")[1];
	}

	return options;
}

/** Return the date as, for example, "12 Feb 2012" if the date is 12th of February 2012  **/
function frontendDate(dateToFormat) {
	if(dateToFormat.split(' ').length > 1) {
		return dateToFormat;
	} else {
		return dateToFormat.split('/')[1] + ' ' + monthnames[dateToFormat.split('/')[0] - 1] + ' ' + dateToFormat.split('/')[2];
	}
}

/** Return the date as, for example, "02/12/2012" if the date is 12th of February 2012 **/
function backendDate(dateToFormat) {
	if(dateToFormat.split('/').length > 1) {
		return dateToFormat;
	} else {
		if(monthnumbers[dateToFormat.split(' ')[1]].length == 1) {
			return '0' + monthnumbers[dateToFormat.split(' ')[1]] + '/' + dateToFormat.split(' ')[0] + '/' + dateToFormat.split(' ')[2];
		} else {
			return monthnumbers[dateToFormat.split(' ')[1]] + '/' + dateToFormat.split(' ')[0] + '/' + dateToFormat.split(' ')[2];
		}
	}
}



function fixAfterWindowResize () {
	/**
	 This function fixes some height/width issues when the screen is being resized, or a scrollbar (dis)appears.

	 Arguments:
	 None.
	 **/

	$("body").css("margin-bottom", $("#north").outerHeight());
	$("#report_panel").css("margin-top", $("#north").outerHeight());
	$("#report_area").css("width", ($(window).width() - ($("#report_area_extension").outerWidth() + $(".workbar").outerWidth())));
	$("#report_area").css("height", ($(document).height() - $("#north").height()));
	$("#report_area_extension").css("height", ($(window).height() - $("#north").height() - 5));
	$("#report_area_extension").css("top", $("#north").height());
	$("#report_area_extension").css("left", $(".workbar").width());
	$("#v3 #notifications_and_warnings").css("margin-top", $("#north").outerHeight() + 10 - 100); // -100 is for body margin compensation

	if($("#v3 #notifications_and_warnings").outerHeight() > 0) {
		$("#v3 #interface_content").css("margin-top", $("#notifications_and_warnings").outerHeight() + 10 - 75); // -100 is for body margin compensation
	} else if($("#v3 .updateProgressText").outerHeight() > 0) {
		$("#v3 #interface_content").css("margin-top", $("#north").outerHeight() + 10);
	} else {
		//$("#v3 #interface_content").css("margin-top", $("#north").outerHeight() + 10 - 100); // -100 is for body margin compensation
		$("#v3 #interface_content").css("margin-top", $("#north").outerHeight() + 10);
	}

	if(($(window).height() - ($("#info_frame").height() + 10)) <= ($("#north").outerHeight() + 290)) {
		$("#info_frame").css("top", ($("#north").outerHeight() + 290));
	} else {
		$("#info_frame").css("top", ($(window).height() - ($("#info_frame").height() + 10)));
	}

	$("#interface_content").css("margin-left", $(".workbar").outerWidth() + 10 - 60); // -100 is for body margin compensation
	$(".workbar").css("top", $("#north").outerHeight());

	$(".lwa_datepicker").css("top", $("#north").outerHeight() + 105);

	$(".lwa_datepicker_overlay").css("height", $(document).height());

	$(".workbar").css("top", $("#north").outerHeight());
	$("#manage_profiles_list").css("top",($("#north").outerHeight() - 1));
}







function toggleReportHelp (eventTarget) {
	/**
	 When the Open Help button of a report is being clicked, we will show or hide the Help of that report.

	 Arguments:
	 - eventTarget
	 The element which caused this function to be called.
	 **/

	if(eventTarget.closest(".box").find(".help_content").is(':visible') == true) {
		eventTarget.closest(".box").find(".help_content").hide();
	} else {
		eventTarget.closest(".box").find(".help_content").show();
		if (eventTarget.closest(".box").find(".ss_overlay").is(':visible') == true) {

			if (eventTarget.closest(".box").find(".ss_overlay .mask .help_content").length==0) {
				hc = eventTarget.closest(".box").find(".help_content");
				hc.css("margin" ,"20px");
				eventTarget.closest(".box").find(".ss_overlay .mask").prepend(hc);
			}
		}
	}
	return false;
}



function toggleReportMail (eventTarget) {
	/**
	 When the Open Mail button of a report is being clicked, we will show or hide the Mail form of that report.

	 Arguments:
	 - eventTarget
	 The element which caused this function to be called.
	 **/

	if(eventTarget.closest(".dialog").find(".mailer").is(':visible') == true) {
		eventTarget.closest(".dialog").find(".mailer").removeClass("opened");
	} else {
		eventTarget.closest(".dialog").find(".mailer").addClass("opened");
	}
	return false;
}



function smallifyReport(largeDialogID) {
	$("#" + largeDialogID).remove();

	var dialogID = largeDialogID.substr(0, largeDialogID.length - 6);

	$("#" + dialogID).show();

	$("#" + dialogID).find('.expand_switch').addClass('expand');
	$("#" + dialogID).find('.expand_switch').removeClass('smallify');
}







function closeReport (dialogID, callback_func) {
	/**
	 This function will remove a report from the DOM.

	 Arguments:
	 - dialogID
	 The ID of the report that needs to be closed.
	 - callback_func [optional]
	 The function that needs to be called, once the report has been removed.
	 **/

	if(ajax_requests["#" + dialogID + " .dialog-content"] != undefined) {
		ajax_requests["#" + dialogID + " .dialog-content"].abort();
	}

	if(dialogID != undefined) {
		/* When the close button of a Dialog is being clicked, we will remove that Dialog. */
		$("#" + dialogID).remove();

		$(".workbar .workicons .icon[rel=" + dialogID + "]").remove();
	}

	/* If there's no report opened anymore, we'll close the dashboard that was opened. */
	if($(".dialog").length < 1) {
		$("#report_area").removeData("opened_dashboard");
		$("#report_area .dashboard.report_icon").removeClass("active");
		$('.delete-dashboard').hide();
	}

	/* If there is a callback function, we'll do that function when we're done closing the report. */
	if(callback_func == undefined) {
		callback_func = function() {};
	}
	callback_func();
}



function minimizeReport (dialogID) {
	/**
	 This function will minimize a report.

	 Arguments:
	 - dialogID
	 The ID of the report that needs to be minimized.
	 **/

	var windata = $("#" + dialogID).attr('id').split('--');

	var iconsource = $("#report_area .report_icon[rel=" + windata[0] + "] img").attr('src');

	$(".workbar .workicons .icon[rel=" + dialogID + "]").addClass("minimized");
	$("#" + dialogID).hide();
}



function restoreReport (dialogID) {
	/**
	 This function will unminimize a minimized report.

	 Arguments:
	 - dialogID
	 The ID of the report that needs to be restored.
	 **/

	$(".workbar .workicons .icon[rel=" + dialogID + "]").removeClass("minimized");
	$("#" + dialogID).fadeIn(500);
}



function closeAll () {
	/**
	 This function will remove all reports from the DOM.

	 Arguments:
	 None.
	 **/

	$(".grid").remove();
	$(".workbar_grid").remove();

	createGrids();

	/* We also want to close the opened dashboard */
	$("#report_area").removeData("opened_dashboard");
	$("#report_area .dashboard.report_icon").removeClass("active");
	$('.delete-dashboard').hide();

	fixAfterWindowResize();
}



function minimizeAll () {
	/**
	 This function will minimize all reports.

	 Arguments:
	 None.
	 **/

	$(".workbar .workicons .icon").addClass("minimized");
	$('.dialog:not(:hidden,#notebook)').hide();
}



function restoreAll () {
	/**
	 This function will unminimize all minimized reports.

	 Arguments:
	 None.
	 **/

	$('.workbar .workicons .icon').each(function() {
		restoreReport($(this).attr("rel"));
	});
}



function bringToFront (object) {
	/**
	 This function changes the z-index, so that the object is in front of other objects.

	 Arguments:
	 - object
	 The JQuery object that needs it z-index changed.
	 **/

	$(".dialog, .ui-dialog").css('z-index',0);
	object.css('z-index',99999);
}





/** <-------------------------------------------------------------------------------- Creation --------------------------------------------------------------------------------> **/






function createGauge (gauge_container, gauge_label, gauge_current, gauge_target, plot_options) {
	if(gauge_label == undefined) { gauge_label = ""; }
	s1 = [gauge_current];
	var ticks;
	if(gauge_current >= gauge_target) {
		var ticks = [0, (gauge_current * 0.25), (gauge_current * 0.5), (gauge_current * 0.75), gauge_current];
		var intervals = [(gauge_target * 0.25), (gauge_target * 0.75), gauge_target, gauge_current];
	} else {
		var ticks = [0, (gauge_target * 0.25), (gauge_target * 0.5), (gauge_target * 0.75), gauge_target];
		var intervals = [(gauge_target * 0.25), (gauge_target * 0.75), gauge_target];
	}
	if (plot_options!== undefined) {
		plots[gauge_container] = $.jqplot(gauge_container,[s1],plot_options);
	} else {
		plots[gauge_container] = $.jqplot(gauge_container,[s1],{
			seriesDefaults: {
				renderer: $.jqplot.MeterGaugeRenderer,
				rendererOptions: {
					label: gauge_label,
					labelPosition: 'bottom',
					labelHeightAdjust: -5,
					ringColor: "#FF8C00",
					tickColor: "#333",
					background: "#FFF",
					intervalOuterRadius: 85,
					ticks: ticks,
					intervals: intervals,
					intervalColors:['#50A5D3', '#1970B4', '#0E4067', '#FF0000']
				},
				pointLabels: { show: false }
			},
			cursor: {
				show: false
			}
		});
	}
}

function createFunnel (funnel_container, funnel_labels, funnel_data, funnel_options) {
	for(var key in funnel_data) {
		funnel_data[key] = parseFloat(funnel_data[key]);
	}
	var s1 = funnel_labels;
	var s2 = funnel_data;

	applyGraphTooltip(funnel_container);

	funnel_options.seriesDefaults.renderer = $.jqplot.FunnelRenderer;

	plots[funnel_container] = $.jqplot(funnel_container, [s2], funnel_options);
}

function createIframeWindow (iframeURL, iframeTitle, report_id) {
	var frid="thisiframe";

	$(".report-options").modal('hide');
	ui.ShowWidget(".iframe-widget");

	$("#widgetModal .iframe-widget").attr("src", iframeURL);

	if(report_id != undefined) {
		$("#widgetModal .iframe-widget").attr("data-report", report_id);
	}

	$("#widgetModal .iframe-widget").attr("id", frid);
	if(iframeTitle != undefined) {
		$("#widgetModal .modal-title").html(iframeTitle);
	}


	$('#widgetModal .iframe-widget').load(function() {
		$("#widgetModal .iframe-widget").attr("style", "height: "+$('#widgetModal .iframe-widget').contents().height()+"px");
	});
}



function plotGraph (graphcontainer, graphdata, actionmenu_type, graphOptions, legendOrientation, legendDisplay) {
	/**
	 This function will create a window for reports with all needed attributes.

	 Arguments:
	 - graphcontainer
	 The ID of the container of the graph we're about to create.
	 - graphdata
	 The data we use to plot our graph.

	 Additional Info:
	 Here'll be a list of built-in JQplot Events:
	 jqplotClick
	 jqplotDblClick
	 jqplotMouseDown
	 jqplotMouseUp
	 jqplotMouseMove
	 jqplotMouseEnter
	 jqplotMouseLeave

	 Add the event like this:
	 $.jqplot.eventListenerHooks.push(['jqplotClick', FUNCTION);

	 Please note that this event has to be initialized BEFORE the graph.
	 **/

	plots; // This is a global variable, initialized at the top of this file.

	var seriedata = []; // This array will contain all data about the series/plots, such as lines, bars, etc.
	var graphtype = []; // For each dataset, we'll have an individual type.
	var graphlabel = []; // For each dataset, we'll have an individual label.
	var plotdata = []; // This will contain all the data we need to plot a graph.
	var new_xaxis_labels = []; // This array will contain the X axis labels to be used in the graph.
	graphdata[0].shift();
	var xaxis_labels = graphdata[0]; // This array will contain the X axis labels to be used in the graph, before they are converted correctly.
	var yaxis_labels = []; // This array will contain the Y axis labels to be used in the graph.

	if(legendOrientation == undefined) {
		legendOrientation = 'south_legend'; // can be either 'south_legend', 'east_legend' or 'west_legend'
	}
	if(legendDisplay == undefined) {
		legendDisplay = 'block'; // can be either 'block', 'inline' or 'none'
	}

	/* This barf is to create padding between the X axis (and allies) labels. */
	for(var i = 0; i < xaxis_labels.length; i++) {
		new_xaxis_labels.push(xaxis_labels[i]);
	}

	var barcount = 0;
	var linecount = 0;
	var maxNumbers = [];
	var graphtrend = {};

	for(i = 1; i < graphdata.length; i++) {
		graphlabel[i] = graphdata[i][0];

		if(graphlabel[i].split("|")[2] != undefined) {
			graphtrend[i] = true;
		} else {
			graphtrend[i] = false;
		}
		graphtype[i] = graphlabel[i].split("|")[1];
		graphlabel[i] = graphlabel[i].split("|")[0];
		graphdata[i].shift();

		for(var array_entry in graphdata[i]) {
			if(is_numeric(graphdata[i][array_entry])) {
				if(graphdata[i][array_entry] == " ") {
					graphdata[i][array_entry] = 0;
				} else {
					graphdata[i][array_entry] = parseFloat(graphdata[i][array_entry]);
				}
			}
		}

		/* We'll count the graphtype and prepare our datasets. */
		if(graphtype[i] == 'bar') {
			barcount++;
			seriedata.push({
				label: graphlabel[i], // This is the type of data (Visitors, for example).
				renderer: $.jqplot.BarRenderer,
				trendline: { show: graphtrend[i] }
			});
		} else if(graphtype[i] == 'line') {
			linecount++;
			seriedata.push({
				label: graphlabel[i], // This is the type of data (Visitors, for example).
				renderer: $.jqplot.LineRenderer
			});
		} else if(graphtype[i] == 'stackedbar') {
			barcount++;
			seriedata.push({
				label: graphlabel[i], // This is the type of data (Visitors, for example).
				renderer: $.jqplot.BarRenderer,
				trendline: { show: graphtrend[i] }
			});
		} else {
			seriedata.push({
				label: graphlabel[i], // This is the type of data (Visitors, for example).
			});
		}

		maxNumbers.push(maxInArray(graphdata[i]));
		plotdata.push(graphdata[i]);
	}

	// We don't want values like 32752.43 in our Y axis, so we'll calculate our own Y axis.
	yaxis_labels = [];
	for(var c = 0; c <= 5; c++) {
		var maxNum = maxInArray(maxNumbers);

		var num = (maxNum * (c / 4.9));
		if(maxNum < 10) {
			num = parseFloat(num.toFixed(2));
		}
		yaxis_labels.push(num);
	}

	for(var serie_id in seriedata) {
		if(graphOptions.series[serie_id] != undefined) {
			for(var serie_key in seriedata[serie_id]) {
				if(graphOptions.series[serie_id][serie_key] == undefined) {
					graphOptions.series[serie_id][serie_key] = seriedata[serie_id][serie_key];
				}
			}
		} else {
			graphOptions.series[serie_id] = seriedata[serie_id];
		}
	}

	if(graphOptions.axes.xaxis.ticks.length == 0) {
		graphOptions.axes.xaxis.ticks = new_xaxis_labels;
	}

	if(graphOptions.axes.yaxis.ticks) {
		//graphOptions.axes.yaxis.ticks = yaxis_labels;
	} else {
		graphOptions.axes.yaxis.ticks = yaxis_labels;
	}

	applyGraphTooltip(graphcontainer);

	if(barcount == 0 && linecount > 0) {
		var graphplotted = 'line';
		plots[graphcontainer] = graph.lineChart(graphcontainer, plotdata, graphOptions);
	} else if(linecount == 0 && barcount > 0) {
		var graphplotted = 'bar';

		plots[graphcontainer] = graph.barChart(graphcontainer, plotdata, graphOptions);
	} else if(barcount > 0 && linecount > 0) {
		var graphplotted = 'barline';
		if(graphOptions.stackSeries == true) {
			var yaxis_base = 0;
			for (var i = 0; i < maxNumbers.length; i++) {

				if(graphOptions.series[i]['disableStack']==true) { continue; }
				yaxis_base = yaxis_base + maxNumbers[i];
			}

			yaxis_labels = [];
			for(var c = 0; c <= 5; c++) {
				var num = (yaxis_base * (c / 4.9));
				if(yaxis_base < 10) {
					num = parseFloat(num.toFixed(2));
				}
				yaxis_labels.push(num);
			}

			if(graphOptions.axes.yaxis.ticks) {
				graphOptions.axes.yaxis.ticks = yaxis_labels;
			}
			if(graphOptions.axes.y2axis) {
				graphOptions.axes.y2axis.ticks = yaxis_labels;
			}
		}
		plots[graphcontainer] = graph.barLineChart(graphcontainer, plotdata, graphOptions);
	} else {
		var graphplotted = 'area';

		var yaxis_base = 0;
		for (var i = 0; i < maxNumbers.length; i++) {
			yaxis_base = yaxis_base + maxNumbers[i];
		}

		yaxis_labels = [];
		for(var c = 0; c <= 5; c++) {
			var num = (yaxis_base * (c / 4.9));
			if(yaxis_base < 10) {
				num = parseFloat(num.toFixed(2));
			}
			yaxis_labels.push(num);
		}

		if(graphOptions.axes.yaxis.ticks) {
			graphOptions.axes.yaxis.ticks = yaxis_labels;
		}
		plots[graphcontainer] = graph.areaChart(graphcontainer, plotdata, graphOptions);
	}

	if($("#" + graphcontainer).find(".jqplot-axis.jqplot-xaxis").find(".jqplot-xaxis-tick").length > 20) {
		var i = 0;
		$("#" + graphcontainer).find(".jqplot-axis.jqplot-xaxis").find(".jqplot-xaxis-tick").each(function() {

			if((i % Math.ceil($("#" + graphcontainer).find(".jqplot-axis.jqplot-xaxis").find(".jqplot-xaxis-tick").length / 10)) != 0) {
				$(this).css('display', 'none');
			}

			i++;
		});
	}

	$("#" + graphcontainer).closest(".graph_area").append("<div class='graph_tooltip'></div>");
}



function showGraphTooltip (ev, seriesIndex, pointIndex, data, plot) {
	if(data != null) {
		var graphcontainer = plot.targetId.substr(1, plot.targetId.length);

		if(plot.plugins.funnelRenderer != null) {
			$("#" + graphcontainer).parent().find(".graph_tooltip").html($("#" + graphcontainer).parent().find(".graphlegend").find("li:eq(" + (data.data[0] - 1) + ")").find("label").html() + ": " + data.data[1]);

			$("#" + graphcontainer).parent().find(".graph_tooltip").css("top", (ev.pageY + 25));

			if(ev.pageX - ($("#" + graphcontainer).parent().find(".graph_tooltip").outerWidth() / 2) < $("#" + graphcontainer).parent().find(".graph_tooltip").outerWidth() / 2) {
				var tooltip_horizon = ev.pageX;
			} else if(ev.pageX - ($("#" + graphcontainer).parent().find(".graph_tooltip").outerWidth() / 2) > $("body").outerWidth() - $("#" + graphcontainer).parent().find(".graph_tooltip").outerWidth()) {
				var tooltip_horizon = ev.pageX - $("#" + graphcontainer).parent().find(".graph_tooltip").outerWidth();
			} else {
				var tooltip_horizon = ev.pageX - ($("#" + graphcontainer).parent().find(".graph_tooltip").outerWidth() / 2);
			}

			$("#" + graphcontainer).parent().find(".graph_tooltip").css("left", tooltip_horizon);
			$("#" + graphcontainer).parent().find(".graph_tooltip").show();
			$("#" + graphcontainer).parent().find(".graph_tooltip").css("display","block");
		} else {
			var xaxis_labels = plots[graphcontainer]['axes']['xaxis']['ticks'];

			var graphlabel = $("#" + graphcontainer).parent().find(".glegend > div:eq(" + data.seriesIndex + ") > label").html();

			var tooltipcontents = "";

			if(pointIndex.xaxis == undefined) {
				$("#" + graphcontainer).parent().find(".graph_tooltip").hide();
				$("#" + graphcontainer).parent().find(".graph_tooltip").css("display","none");
			} else {
				$("#" + graphcontainer).parent().find(".graph_tooltip").css("top", (ev.pageY + 25));

				if(ev.pageX - ($("#" + graphcontainer).parent().find(".graph_tooltip").outerWidth() / 2) < $("#" + graphcontainer).parent().find(".graph_tooltip").outerWidth() / 2) {
					var tooltip_horizon = ev.pageX;
				} else if(ev.pageX - ($("#" + graphcontainer).parent().find(".graph_tooltip").outerWidth() / 2) > $("body").outerWidth() - $("#" + graphcontainer).parent().find(".graph_tooltip").outerWidth()) {
					var tooltip_horizon = ev.pageX - $("#" + graphcontainer).parent().find(".graph_tooltip").outerWidth();
				} else {
					var tooltip_horizon = ev.pageX - ($("#" + graphcontainer).parent().find(".graph_tooltip").outerWidth() / 2);
				}

				$("#" + graphcontainer).parent().find(".graph_tooltip").css("left", tooltip_horizon);
				$("#" + graphcontainer).parent().find(".graph_tooltip").show();
				$("#" + graphcontainer).parent().find(".graph_tooltip").css("display","block");
			}

			if(xaxis_labels[(data.data[0] - 1)] != undefined && data.data[1] != undefined) {
				tooltipcontents = "<strong>" + xaxis_labels[(data.data[0] - 1)] + "</strong><br/>" + data.data[1] + " " + graphlabel;
			} else {
				var total_for_serie = 0;
				for(var c = 0; c < data.data.length; c++) {
					total_for_serie += data.data[c][1];
				}

				tooltipcontents = "Total for " + graphlabel + ": " + total_for_serie;
			}

			$("#" + graphcontainer).parent().find(".graphlegend li").css("font-weight", "normal");
			if(data.seriesIndex != undefined && data.seriesIndex != null) {
				$("#" + graphcontainer).parent().find(".graphlegend li:eq(" + data.pointIndex + ")").css("font-weight", "bold");
			} else {
				$("#" + graphcontainer).parent().find(".graphlegend li:eq(" + data.seriesIndex + ")").css("font-weight", "bold");
			}
			$("#" + graphcontainer).parent().find(".graph_tooltip").html(tooltipcontents);
		}
	} else {
		$("#" + graphcontainer).parent().find(".graph_tooltip").hide();
		$("#" + graphcontainer).parent().find(".graph_tooltip").css("display","none");
		$("#" + graphcontainer).parent().find(".graphlegend li").css("font-weight", "normal");
	}
}



function hideGraphTooltip (ev, gridpos, datapos, neighbor, plot) {
	var graphcontainer = plot.targetId.substr(1, plot.targetId.length);

	$("#" + graphcontainer).parent().find(".graph_tooltip").hide();
	$("#" + graphcontainer).parent().find(".graph_tooltip").css("display","none");
	$("#" + graphcontainer).parent().find(".graphlegend li").css("font-weight", "normal");
}



function applyGraphTooltip (graphcontainer) {
	$.jqplot.eventListenerHooks.push(['jqplotMouseMove', showGraphTooltip]);
	$.jqplot.eventListenerHooks.push(['jqplotMouseLeave', hideGraphTooltip]);
}



function createBalloon (content, balloonClass, balloonType) {
	/**
	 This function will create a Balloon (feedback message).

	 Arguments:
	 - content
	 The content we want to show in our Balloon.
	 - balloonClass [optional]
	 We can add an additional class to the balloon, to use other styles, among other things.
	 - balloonType [optional]
	 You can also use a type, which is either "message" or "alert".
	 **/

	if(balloonType == undefined) { balloonType = "message"; }
	if(balloonClass != undefined) { balloonClass = " " + balloonClass; } else { balloonClass = ""; }

	var uiclass = balloonType == "alert" ? "ui-state-error" : "ui-state-highlight";
	var balloonIcon = balloonType == "alert" ? "ui-icon-alert" : "ui-icon-info";

	if($(".balloon").length == 0) { // There can be only one Balloon opened, so, if a Balloon is opened, we'll rewrite the contents.
		$("#interface #content").append("<div class='balloon" + balloonClass + " " + uiclass + "'>" + "<span style='float: left; margin-right: 0.3em;' class='ui-icon " + balloonIcon + "'></span>" + content + "</div>");
	} else {
		$(".balloon").attr("class","balloon" + balloonClass + " " + uiclass);
		$(".balloon").html(content);
	}

	var balloonFadeTimer = setTimeout(function() {
		$(".balloon").remove();
	}, 5000);
}


/** <-------------------------------------------------------------------------------- Content --------------------------------------------------------------------------------> **/

function windowHeader (from, to) {
	/**
	 This function returns a window header.

	 Arguments:
	 - from
	 The 'from' date.
	 - to
	 The 'to' date.
	 **/

	return "<div class='report-header'>&nbsp;</div>";
}



function defaultReportLoadingScreen (reportName, daterangeFrom, daterangeTo) {
	/**
	 This function returns a default loading screen.

	 Arguments:
	 - reportName
	 The report name.
	 - daterangeFrom
	 The 'from' date.
	 - daterangeTo
	 The 'to' date.
	 **/

	return windowHeader(daterangeFrom, daterangeTo) + Loading(reportName);
}











function changeUpdateText(statusText) {
	$(".progress_status").html("(" + statusText + ")");
}

function updateProgress(progress, setBar) {
	if(setBar == undefined) { setBar = false; }

	if(setBar == true) {
		$(".updating_progress .ui-progressbar-value").css("width", progress.percentage + "%");
	}

	$(".updating_progress .ui-progressbar-value").stop();

	$(".updating_progress .ui-progressbar-value").animate({
		width: '100%'
	}, (progress.estimate_time * 1000), function() {

	});

	var estimated_perc = Math.round(($(".updating_progress .ui-progressbar-value").width() / $(".updating_progress").width()) * 100);

	$(".updating_progress").attr("title", "Estimated progress for " + progress.file + ": " + estimated_perc + "%");
	$(".progress_percentage").html(estimated_perc + "%");
	$(".updateProgressText").attr("title", "Progress for " + progress.file + ": " + estimated_perc + "% (Total average progress: " + progress.total_percentage + "%)");
	$(".progress_status").html("(" + progress.action + ")");

	if(progress.action.indexOf("Update finished") > -1) {
		$(".updateProgressText > img").remove();
		$(".updateProgressText").html(progress.action);
		$(".updateProgressText").removeAttr('title');
		$(".updating_progress").remove();
		clearInterval(updateCheck);
		fixAfterWindowResize();
	}
}







/** <-------------------------------------------------------------------------------- Overall --------------------------------------------------------------------------------> **/





function is_object (mixed_var) { if (mixed_var instanceof Array) { return false; } else { return (mixed_var !== null) && (typeof(mixed_var) == 'object'); } }



function is_array (input) { return typeof(input) == 'object' && (input instanceof Array); }



function replaceAll (rottenApple, cleanApple, basketOfApples) { basketOfApples = basketOfApples.split(rottenApple).join(cleanApple); return basketOfApples; }



function maxInArray(arr) { var max = 0; for(c = 0; c < arr.length; c++) { if(parseFloat(arr[c]) >= max) { max = parseFloat(arr[c]); } } return max; }



function in_array (needle, haystack) {
	for(var straw in haystack) {
		if(haystack[straw] == needle) {
			return true;
		}
	}

	return false;
}



function is_numeric (mixed_var) { return (typeof(mixed_var) === 'number' || typeof(mixed_var) === 'string') && mixed_var !== '' && !isNaN(mixed_var); }



function number_format (number, decimals, dec_point, thousands_sep) {
	number = (number + '').replace(/[^0-9+\-Ee.]/g, '');
	var n = !isFinite(+number) ? 0 : +number,
		prec = !isFinite(+decimals) ? 0 : Math.abs(decimals),        sep = (typeof thousands_sep === 'undefined') ? ',' : thousands_sep,
		dec = (typeof dec_point === 'undefined') ? '.' : dec_point,
		s = '',
		toFixedFix = function (n, prec) {
			var k = Math.pow(10, prec);            return '' + Math.round(n * k) / k;
		};
	// Fix for IE parseFloat(0.55).toFixed(0) = 0;
	s = (prec ? toFixedFix(n, prec) : '' + Math.round(n)).split('.');
	if (s[0].length > 3) {        s[0] = s[0].replace(/\B(?=(?:\d{3})+(?!\d))/g, sep);
	}
	if ((s[1] || '').length < prec) {
		s[1] = s[1] || '';
		s[1] += new Array(prec - s[1].length + 1).join('0');    }
	return s.join(dec);
}



function php_urlencode (str) {
	return encodeURIComponent(str);
	str = escape(str);
	return str.replace(
		/[*+\/@]|%20/g,
		function (s) {
			switch (s) {
				case "*": s = "%2A"; break;
				case "+": s = "%2B"; break;
				case "/": s = "%2F"; break;
				case "@": s = "%40"; break;
				case "%20": s = "+"; break;
			}
			return s;
		}
	);
}



function strtotime (str, now) {
	// Convert string representation of date and time to a timestamp

	// version: 1107.2516
	// discuss at: http://phpjs.org/functions/strtotime    // +   original by: Caio Ariede (http://caioariede.com)
	// +   improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
	// +      input by: David
	// +   improved by: Caio Ariede (http://caioariede.com)
	// +   improved by: Brett Zamir (http://brett-zamir.me)    // +   bugfixed by: Wagner B. Soares
	// +   bugfixed by: Artur Tchernychev
	// %        note 1: Examples all have a fixed timestamp to prevent tests to fail because of variable time(zones)
	// *     example 1: strtotime('+1 day', 1129633200);
	// *     returns 1: 1129719600    // *     example 2: strtotime('+1 week 2 days 4 hours 2 seconds', 1129633200);
	// *     returns 2: 1130425202
	// *     example 3: strtotime('last month', 1129633200);
	// *     returns 3: 1127041200
	// *     example 4: strtotime('2009-05-04 08:30:00');    // *     returns 4: 1241418600

	strTmp = str;
	strTmp = strTmp.replace(/\s{2,}|^\s|\s$/g, ' '); // unecessary spaces
	strTmp = strTmp.replace(/[\t\r\n]/g, ''); // unecessary chars
	if (strTmp == 'now') {
		return (new Date()).getTime() / 1000; // Return seconds, not milli-seconds
	} else if (!isNaN(parse = Date.parse(strTmp))) {
		return (parse / 1000);
	} else if (now) {
		now = new Date(now * 1000); // Accept PHP-style seconds
	} else {
		now = new Date();
	}

	strTmp = strTmp.toLowerCase();

	var __is = {
		day: {
			'sun': 0,
			'mon': 1,
			'tue': 2,
			'wed': 3,
			'thu': 4,
			'fri': 5,
			'sat': 6
		},
		mon: {
			'jan': 0,
			'feb': 1,
			'mar': 2,
			'apr': 3,
			'may': 4,
			'jun': 5,
			'jul': 6,
			'aug': 7,
			'sep': 8,
			'oct': 9,
			'nov': 10,
			'dec': 11
		}
	};
	var process = function (m) {
		var ago = (m[2] && m[2] == 'ago');
		var num = (num = m[0] == 'last' ? -1 : 1) * (ago ? -1 : 1);

		switch (m[0]) {
			case 'next':
				switch (m[1].substring(0, 3)) {
					case 'yea':
						now.setFullYear(now.getFullYear() + num);
						break;
					case 'mon':
						now.setMonth(now.getMonth() + num);
						break;
					case 'wee':                now.setDate(now.getDate() + (num * 7));
						break;
					case 'day':
						now.setDate(now.getDate() + num);
						break;
					case 'hou':
						now.setHours(now.getHours() + num);
						break;
					case 'min':
						now.setMinutes(now.getMinutes() + num);
						break;
					case 'sec':
						now.setSeconds(now.getSeconds() + num);
						break;
					default:
						var day;
						if (typeof(day = __is.day[m[1].substring(0, 3)]) != 'undefined') {
							var diff = day - now.getDay();
							if (diff == 0) {
								diff = 7 * num;
							} else if (diff > 0) {
								if (m[0] == 'last') {
									diff -= 7;
								}
							} else {
								if (m[0] == 'next') {
									diff += 7;
								}
							}
							now.setDate(now.getDate() + diff);
						}
				}
				break;

			default:
				if (/\d+/.test(m[0])) {
					num *= parseInt(m[0], 10);

					switch (m[1].substring(0, 3)) {
						case 'yea':
							now.setFullYear(now.getFullYear() + num);
							break;
						case 'mon':
							now.setMonth(now.getMonth() + num);
							break;
						case 'wee':
							now.setDate(now.getDate() + (num * 7));
							break;
						case 'day':
							now.setDate(now.getDate() + num);
							break;
						case 'hou':
							now.setHours(now.getHours() + num);
							break;
						case 'min':
							now.setMinutes(now.getMinutes() + num);
							break;
						case 'sec':
							now.setSeconds(now.getSeconds() + num);
							break;
					}
				} else {
					return false;
				}
				break;
		}
		return true;
	};

	match = strTmp.match(/^(\d{2,4}-\d{2}-\d{2})(?:\s(\d{1,2}:\d{2}(:\d{2})?)?(?:\.(\d+))?)?$/);
	if (match != null) {
		if (!match[2]) {
			match[2] = '00:00:00';
		} else if (!match[3]) {
			match[2] += ':00';
		}

		s = match[1].split(/-/g);

		for (i in __is.mon) {
			if (__is.mon[i] == s[1] - 1) {
				s[1] = i;
			}
		}
		s[0] = parseInt(s[0], 10);
		s[0] = (s[0] >= 0 && s[0] <= 69) ? '20' + (s[0] < 10 ? '0' + s[0] : s[0] + '') : (s[0] >= 70 && s[0] <= 99) ? '19' + s[0] : s[0] + '';
		return parseInt(this.strtotime(s[2] + ' ' + s[1] + ' ' + s[0] + ' ' + match[2]) + (match[4] ? match[4] / 1000 : ''), 10);
	}
	var regex = '([+-]?\\d+\\s' + '(years?|months?|weeks?|days?|hours?|min|minutes?|sec|seconds?' + '|sun\\.?|sunday|mon\\.?|monday|tue\\.?|tuesday|wed\\.?|wednesday' + '|thu\\.?|thursday|fri\\.?|friday|sat\\.?|saturday)' + '|(last|next)\\s' + '(years?|months?|weeks?|days?|hours?|min|minutes?|sec|seconds?' + '|sun\\.?|sunday|mon\\.?|monday|tue\\.?|tuesday|wed\\.?|wednesday' + '|thu\\.?|thursday|fri\\.?|friday|sat\\.?|saturday))' + '(\\sago)?';

	match = strTmp.match(new RegExp(regex, 'gi')); // Brett: seems should be case insensitive per docs, so added 'i'
	if (match == null) {
		return false;
	}

	for (i = 0; i < match.length; i++) {
		if (!process(match[i].split(' '))) {
			return false;
		}
	}

	return (now.getTime() / 1000);
}

function plusMinusForm (container_id, field_a, field_b, field_c, andor_selector_available, start_count) {
	if(andor_selector_available == undefined) { andor_selector_available = true; }

	$("#" + container_id + " .conditions_row:first .plus_btn").removeAttr("disabled");

	if($("#" + container_id + " .conditions_row").length == 1) {
		$("#" + container_id + " .min_btn").attr("disabled", "disabled");
	} else {
		if(andor_selector_available != false && $("#" + container_id + " .andor_selector").length < 1) {
			$("#" + container_id + " .condition_descriptor").before(
				"<div class='andor_selector'>" +
				"<span style='float: left; margin-right: 20px;'>Mode: </span>" +
				"<label style='float: left;'>AND</label><input type='radio' style='width: 25px; float: left;' name='andor' value='AND' checked />" +
				"<label style='float: left;'>OR</label><input type='radio' style='width: 25px; float: left;' name='andor' value='OR' />" +
				"</div>" +
				"<div class='clear'></div>"
			);
		}
	}

	$("#" + container_id).on("click",".plus_btn", function(e) {
		e.preventDefault();

		var container_id = $(this).parents(".report-options").first().attr("id");

		$(this).closest("#" + container_id + " .conditions_row").after( $(this).closest(".conditions_row").clone(true) );

		if(start_count == undefined){
			var c = 1;
		} else {
			var c = 0;
		}

		$("#" + container_id + " .conditions_row").each(function() {
			$(this).find("*[src='" + field_a + "']").attr('rel', c);
			$(this).find("*[src='" + field_a + "']").attr('id', field_a + c);
			$(this).find("*[src='" + field_a + "']").attr('name', field_a + c);

			$(this).find("*[src='" + field_b + "']").attr('rel', c);
			$(this).find("*[src='" + field_b + "']").attr('id', field_b + c);
			$(this).find("*[src='" + field_b + "']").attr('name', field_b + c);

			$(this).find("*[src='" + field_c + "']").attr('rel', c);
			$(this).find("*[src='" + field_c + "']").attr('id', field_c + c);
			$(this).find("*[src='" + field_c + "']").attr('name', field_c + c);
			c++;
		});

		if($("#" + container_id + " .conditions_row").length > 1) {
			$("#" + container_id + " .conditions_row").find(".min_btn").removeAttr("disabled");
		}

		if(andor_selector_available != false && $("#" + container_id + " .andor_selector").length < 1) {
			$("#" + container_id + " .condition_descriptor").before(
				"<div class='andor_selector'>" +
				"<span style='float: left; margin-right: 20px;'>Mode: </span>" +
				"<label style='float: left;'>AND</label><input type='radio' style='width: 25px; float: left;' name='andor' value='AND' checked />" +
				"<label style='float: left;'>OR</label><input type='radio' style='width: 25px; float: left;' name='andor' value='OR' />" +
				"</div>" +
				"<div class='clear'></div>"
			);
		}
	});

	$("#" + container_id).on("click",".min_btn", function() {
		if($(this).attr("disabled") == "disabled"){
			return false;
		}

		var container_id = $(this).parents(".report-options").first().attr("id");

		if($("#" + container_id+" .conditions_row").length != 1) {
			$(this).closest(".conditions_row").remove();
		}

		var c = 1;
		$("#" + container_id + " .conditions_row").each(function() {
			$(this).find("*[src='" + field_a + "']").attr('rel', c);
			$(this).find("*[src='" + field_a + "']").attr('id', field_a + c);
			$(this).find("*[src='" + field_a + "']").attr('name', field_a + c);

			$(this).find("*[src='" + field_b + "']").attr('rel', c);
			$(this).find("*[src='" + field_b + "']").attr('id', field_b + c);
			$(this).find("*[src='" + field_b + "']").attr('name', field_b + c);

			$(this).find("*[src='" + field_c + "']").attr('rel', c);
			$(this).find("*[src='" + field_c + "']").attr('id', field_c + c);
			$(this).find("*[src='" + field_c + "']").attr('name', field_c + c);

			c++;
		});

		if($("#" + container_id+" .conditions_row").length == 1) {
			$("#" + container_id+ " .plus_btn").removeAttr("disabled");
			$("#" + container_id +" .min_btn").attr("disabled", "disabled");

			$("#" + container_id+" .andor_selector").remove();
		}
	});
}


//dynamic menu system
var contextMenuObj;  // Global for the current popup displayed.
var contextMenuHTTPRequest; // The ajax object for any popup menu lookups.

function GetXmlHttpObject()
{
	var objXMLHttp=null
	if (window.XMLHttpRequest)
	{
		objXMLHttp=new XMLHttpRequest()
	}
	else if (window.ActiveXObject)
	{
		objXMLHttp=new ActiveXObject("Microsoft.XMLHTTP")
	}
	return objXMLHttp
}

function urldecode (str) {
	return decodeURIComponent((str + '').replace(/\+/g, '%20'));
}

function toFormDynamic(field,val, event) {
	/* 	ok ok explain this voodoo me..
		currently clicked event is inside the actionmenu so get the parent element of the actionmenu wrapper and then search there for your field. ;)
	*/
	$(event).parents(".actionmenu").first().parent().find("[name='"+ field +"']").val(urldecode(val));
	hide_popup_menu();
}

// Unhook an event
function removeEvent(obj, evType, fn, useCapture) {
	if (obj.removeEventListener) {
		obj.removeEventListener(evType, fn, useCapture);
		return true;
	} else if (obj.detachEvent) {
		var r = obj.detachEvent('on'+evType, fn);
		return r;
	} else {
		obj['on'+evType] = "";
	}
}

// Get the target control from an event in a browser independant way.
function eventTarget(evt) {
	if (window.event && window.event.srcElement)
		return window.event.srcElement;
	else if (evt && evt.target)
		return evt.target;
	else
		return undefined;
}

function popupActionMenu(evt, str, pagetype, additional_parameters) {
	// Is the menu currently showing?  If so, hide it.
	if (contextMenuObj) {
		hide_popup_menu();
	}

	// clickTarget is the object clicked.  There might be things about it
	// that are interesting to discover - like a certain url (if it's an <A> tag)
	// or an ID or NAME that gives us information about what to display in the menu
	var clickTarget;
	clickTarget = evt.currentTarget;

	// Hook up to mousedown event for the whole browser.  This lets us close the menu if
	// clicking outside of it.
	addEvent(document, 'mousedown', click_with_menu_enabled, false);

	// Create the top level menu
	contextMenuObj = document.createElement('span');
	contextMenuObj.id = "popupmainmenu";

	if (pagetype=="forminput") {
		contextMenuObj.className = "actionmenu forminputmenu ui-corner-all";
	} else {
		contextMenuObj.className = "actionmenu ui-corner-all";
	}
	contextMenuObj.display = "block";
	contextMenuObj.overflow = "visible";

	// Add the menu itself to the page.
	clickTarget.parentNode.appendChild(contextMenuObj);

	// Create the ajax request object
	contextMenuHTTPRequest=GetXmlHttpObject()
	if (contextMenuHTTPRequest==null) {
		alert ("Browser does not support HTTP Request")
		return;
	}

	// Build the url query to the server (this, could, build the menu itself too...)
	var ajax_url = actionmenu_url;
	if (pagetype=="page") {
		ajax_url += "?url="+encodeURIComponent(str);
	} else if (pagetype=="keyword") {
		ajax_url += "?keyword="+encodeURIComponent(str);
	} else if (pagetype=="referrer") {
		ajax_url += "?referrer="+encodeURIComponent(str);
	} else if (pagetype=="statuscode") {
		ajax_url += "?statuscode="+encodeURIComponent(str);
	} else if (pagetype=="forminput") {
		ajax_url += "?forminput="+encodeURIComponent(str);
	} else if (pagetype=="ipnumber") {
		ajax_url += "?ipnumber="+encodeURIComponent(str);
	} else if (pagetype=="kpi") {
		ajax_url += "?kpi="+encodeURIComponent(str);
	} else if (pagetype=="clicktrailpage") {
		ajax_url += "?clicktrailurl="+encodeURIComponent(str);
	} else if (pagetype=="clicktrailreferrer") {
		ajax_url += "?clicktrailreferrer="+encodeURIComponent(str);
	} else if (pagetype=="clicktrailipnumber") {
		ajax_url += "?clicktrailipnumber="+encodeURIComponent(str);
	} else if (pagetype=="country") {
		ajax_url += "?country="+encodeURIComponent(str);
	} else if (pagetype=="error") {
		ajax_url += "?error="+encodeURIComponent(str);
	} else if (pagetype=="kpi") {
		ajax_url += "?kpi="+encodeURIComponent(str);
	} else if (pagetype=="crawler") {
		ajax_url += "?crawler="+encodeURIComponent(str);
	} else if (pagetype=="utm_content") {
		ajax_url += "?utm_content="+encodeURIComponent(str);
	} else if (pagetype=="utm_source") {
		ajax_url += "?utm_source="+encodeURIComponent(str);
	} else if (pagetype=="utm_medium") {
		ajax_url += "?utm_medium="+encodeURIComponent(str);
	} else if (pagetype=="utm_campaign") {
		ajax_url += "?utm_campaign="+encodeURIComponent(str);
	} else if (pagetype=="utm_term") {
		ajax_url += "?utm_term="+encodeURIComponent(str);
	}


	ajax_url += "&conf="+encodeURIComponent(conf_name);
	ajax_url += "&minimumDate="+encodeURIComponent($(clickTarget).closest(".box").find(".fromRange").attr("value"));
	ajax_url += "&maximumDate="+encodeURIComponent($(clickTarget).closest(".box").find(".toRange").attr("value"));
	if ($(clickTarget).attr("data-contextDate")) {
		ajax_url += "&contextDate="+encodeURIComponent( $(clickTarget).attr("data-contextDate") );
	}

	if(additional_parameters != undefined) {
		ajax_url += additional_parameters;
	}

	// Hook up the ajax request object
	contextMenuHTTPRequest.onreadystatechange=populatePopupMenu
	contextMenuHTTPRequest.open("GET",ajax_url,true)
	contextMenuHTTPRequest.send(null)

	// Don't do any of the upstream "addEvent" calls - just the one we're on.  This means
	// if parent containers (spans, divs, etc) have logic, then we don't call it.
	if (evt.stopPropagation && evt.preventDefault) {
		evt.stopPropagation();
		evt.preventDefault();
	}
	if (window.event) {
		window.event.cancelBubble = true;
		window.event.returnValue = false;
	}
	return false;
}

var actionmenu_url = 'includes/actionmenu_v6.php';

function popupMenu(evt, str, pagetype, additional_parameters) {
	// make a clean start
	evt.preventDefault();
	evt.stopPropagation();
	$(".actionmenu").remove();

	var clickTarget;
	if (window.event && window.event.srcElement) {
		clickTarget = window.event.srcElement;
	} else if (evt && evt.target) {
		clickTarget = evt.target;
	}


	// Build the url query to the server (this, could, build the menu itself too...)
	var ajax_url = actionmenu_url;
	if (pagetype=="page") {
		ajax_url += "?url="+encodeURIComponent(str);
	} else if (pagetype=="kpi") {
		ajax_url += "?kpi="+escape(str);
	} else if (pagetype=="keyword") {
		ajax_url += "?keyword="+escape(str);
	} else if (pagetype=="referrer") {
		ajax_url += "?referrer="+escape(str);
	} else if (pagetype=="statuscode") {
		ajax_url += "?statuscode="+escape(str);
	} else if (pagetype=="forminput") {
		ajax_url += "?forminput="+encodeURIComponent(str);
	} else if (pagetype=="ipnumber") {
		ajax_url += "?ipnumber="+escape(str);
	} else if (pagetype=="clicktrailpage") {
		ajax_url += "?clicktrailurl="+escape(str);
	} else if (pagetype=="clicktrailreferrer") {
		ajax_url += "?clicktrailreferrer="+escape(str);
	} else if (pagetype=="clicktrailipnumber") {
		ajax_url += "?clicktrailipnumber="+escape(str);
	} else if (pagetype=="country") {
		ajax_url += "?country="+encodeURIComponent(str);
	}

	var c = $(clickTarget).parents(".report").first().find("[name='conf']").val();
	if(c == undefined){
		c = conf_name;
	}

	ajax_url += "&conf="+escape(c);

	if(additional_parameters != undefined && additional_parameters.indexOf("&minimumDate=") < 0) {
		ajax_url += "&from="+escape(from_date);
	}
	if(additional_parameters != undefined && additional_parameters.indexOf("&maximumDate=") < 0) {
		ajax_url += "&to="+escape(to_date);
	}
	if(additional_parameters != undefined) {
		ajax_url += additional_parameters;
	} else {
		ajax_url += "&from="+escape(from_date);
		ajax_url += "&to="+escape(to_date);
	}

	$(clickTarget).parents("td").first().append("<div class='actionmenu dropdown' style='position:absolute;background-color:#FFF'></div>");
	$(clickTarget).parents("td").first().find(".actionmenu").load(ajax_url);

	$(document).on('click', function (e) {

		if (e.target == clickTarget) {
			//console.log('same');
		} else {
			$(".actionmenu").remove();
			//$(document).unbind( "click" );
		}

	});



}
function oldpopupMenu(evt, str, pagetype, additional_parameters) {
	evt.preventDefault();
	alert('hallo' );
	// Is the menu currently showing?  If so, hide it.
	//if (contextMenuObj) {
	//  hide_popup_menu();
	//}

	// clickTarget is the object clicked.  There might be things about it
	// that are interesting to discover - like a certain url (if it's an <A> tag)
	// or an ID or NAME that gives us information about what to display in the menu
	var clickTarget;
	if (window.event && window.event.srcElement) {
		clickTarget = window.event.srcElement;
	} else if (evt && evt.target) {
		clickTarget = evt.target;
	}

	// If the clicked on item isn't interesting, we might need to move to it's parent.
	// This happens when there are text links, or images, or similar - that are clicked
	// and don't have interesting information, but the "container" does.
	// This is commented out because I don't know what will be interesting to look at from here.
	//while ((clickTarget) && (!clickTarget.id)) {
	//clickTarget = clickTarget.parentNode;
	//if (clickTarget.id) alert(clickTarget.id)
	//}
	// if ((!clickTarget) || (!clickTarget.id) )
	//   return;

	// Hook up to mousedown event for the whole browser.  This lets us close the menu if
	// clicking outside of it.
	addEvent(document, 'mousedown', click_with_menu_enabled, false);

	// Create the top level menu
	contextMenuObj = document.createElement('span');
	contextMenuObj.id = "popupmainmenu";
	/*contextMenuObj.style.position = "absolute";*/
	if (pagetype=="forminput") {
		contextMenuObj.className = "actionmenu forminputmenu ui-corner-all";
	} else {
		contextMenuObj.className = "actionmenu ui-corner-all";
	}
	contextMenuObj.display = "block";
	contextMenuObj.overflow = "visible";
	/*contextMenuObj.innerHTML = "<font color=gray>Accessing...</font></span>"; */
	/*contextMenuObj.style.position = "absolute";*/

	// Add a <br> to the page first, to make sure the menu shows up on the *next* line.
	clickTarget.parentNode.appendChild(document.createElement('br'));
	// Add the menu itself to the page.
	clickTarget.parentNode.appendChild(contextMenuObj);

	// Create the ajax request object
	contextMenuHTTPRequest=GetXmlHttpObject()
	if (contextMenuHTTPRequest==null)
	{
		alert ("Browser does not support HTTP Request")
		return
	}

	// Build the url query to the server (this, could, build the menu itself too...)
	var ajax_url = actionmenu_url;
	if (pagetype=="page") {
		ajax_url += "?url="+escape(str);
	} else if (pagetype=="kpi") {
		ajax_url += "?kpi="+escape(str);
	} else if (pagetype=="keyword") {
		ajax_url += "?keyword="+escape(str);
	} else if (pagetype=="referrer") {
		ajax_url += "?referrer="+escape(str);
	} else if (pagetype=="statuscode") {
		ajax_url += "?statuscode="+escape(str);
	} else if (pagetype=="forminput") {
		ajax_url += "?forminput="+escape(str);
	} else if (pagetype=="ipnumber") {
		ajax_url += "?ipnumber="+escape(str);
	} else if (pagetype=="clicktrailpage") {
		ajax_url += "?clicktrailurl="+escape(str);
	} else if (pagetype=="clicktrailreferrer") {
		ajax_url += "?clicktrailreferrer="+escape(str);
	} else if (pagetype=="clicktrailipnumber") {
		ajax_url += "?clicktrailipnumber="+escape(str);
	}

	ajax_url += "&conf="+escape(conf_name);

	if(additional_parameters != undefined && additional_parameters.indexOf("&minimumDate=") < 0) {
		ajax_url += "&from="+escape(from_date);
	}
	if(additional_parameters != undefined && additional_parameters.indexOf("&maximumDate=") < 0) {
		ajax_url += "&to="+escape(to_date);
	}
	if(additional_parameters != undefined) {
		ajax_url += additional_parameters;
	} else {
		ajax_url += "&from="+escape(from_date);
		ajax_url += "&to="+escape(to_date);
	}
	// Hook up the ajax request object
	contextMenuHTTPRequest.onreadystatechange=populatePopupMenu
	contextMenuHTTPRequest.open("GET",ajax_url,true)
	contextMenuHTTPRequest.send(null)

	// Don't do any of the upstream "addEvent" calls - just the one we're on.  This means
	// if parent containers (spans, divs, etc) have logic, then we don't call it.
	if (evt.stopPropagation && evt.preventDefault) {
		evt.stopPropagation();
		evt.preventDefault();
	}
	if (window.event) {
		window.event.cancelBubble = true;
		window.event.returnValue = false;
	}
	return false;
}

// Add an event to an object
function addEvent(obj, evType, fn, useCapture) {
	if (obj.addEventListener) {
		obj.addEventListener(evType, fn, useCapture);
		return true;
	} else if (obj.attachEvent) {
		var r = obj.attachEvent('on'+evType, fn);
		return r;
	} else {
		obj['on'+evType] = fn;
	}
}

// This is called when the http request object is done.
function populatePopupMenu()
{
	if ((contextMenuHTTPRequest) && (contextMenuHTTPRequest.readyState==4 || contextMenuHTTPRequest.readyState=="complete"))
	{
		if (contextMenuObj) {
			contextMenuObj.innerHTML = contextMenuHTTPRequest.responseText;
			contextMenuHTTPRequest = null;
		}
	}
}

// When anything is clicked (inside or outside the menu), then this is called.
function click_with_menu_enabled(evt) {
	if (contextMenuObj) {

		var target = eventTarget(evt);
		var menuItem = target;
		//alert(target.id);
		if (target.id=="closeme") {
			//alert('we found a close');
			hide_popup_menu();
		} else {

			// Iterate through the object chain to see if the "popupmenumain" object is somewhere in there.  Did we click on something in the menu?
			while ((menuItem) && (menuItem.parentNode) && (menuItem.parentNode.id != "popupmainmenu")) { menuItem = menuItem.parentNode; }

			if  (menuItem && (menuItem.parentNode)&& (menuItem.parentNode.id == "popupmainmenu")) {
				// It's our menu being clicked!  Don't hide the menu.
				return;

			}

			hide_popup_menu();
		}
	}
}

// Hide / cleanup menu, and anything else we created when the menu opened.
function hide_popup_menu() {
	if (contextMenuObj && contextMenuObj.parentNode) {
		var menuParent = contextMenuObj.parentNode;

		menuParent.removeChild(contextMenuObj); // Get rid of the menu itself

		contextMenuObj = null;
		removeEvent(document, 'mousedown', click_with_menu_enabled, false);
		contextMenuHTTPRequest = null; // Make sure we get rid of the HTTP request too, just in case it's in the middle of something.
		//alert('hiding') ;
	}
}



function is_empty (mixed_var) {
	var key;
	if (mixed_var === "" || mixed_var === 0 || mixed_var === "0" || mixed_var === null || mixed_var === false || typeof mixed_var === 'undefined') {
		return true;
	}

	if (typeof mixed_var == 'object') {
		for (key in mixed_var) {
			return false;
		}
		return true;
	}
	return false;
}

function is_int(input){
	return typeof(input) == 'number' && parseInt(input) == input;
}

// this adds a custom data type to datatable that we use for sorting
// $.fn.dataTableExt.aTypes.unshift(
// 	function ( sData )
// 	{
// 		if ( sData.indexOf('sort=') != -1 )
// 		{
// 			return 'sort-numeric';

// 		}
// 		return null;
// 	}
// );

// remove errors when loading datatables
// $.fn.dataTableExt.sErrMode = 'throw';

// // this defines the sorting function we use with our custom data type
// jQuery.fn.dataTableExt.oSort['sort-numeric-asc']  = function(x,y) {
// 	var x = x.match(/sort="(.*?)"/)[1];
// 	var y = y.match(/sort="(.*?)"/)[1];
// 	x = parseFloat( x );
// 	y = parseFloat( y );
// 	return ((x < y) ? -1 : ((x > y) ?  1 : 0));
// };

// jQuery.fn.dataTableExt.oSort['sort-numeric-desc'] = function(x,y) {
// 	var x = x.match(/sort="(.*?)"/)[1];
// 	var y = y.match(/sort="(.*?)"/)[1];
// 	x = parseFloat( x );
// 	y = parseFloat( y );
// 	return ((x < y) ?  1 : ((x > y) ? -1 : 0));
// };

function addslashes (str) {
	// http://kevin.vanzonneveld.net
	return (str + '').replace(/[\\"']/g, '\\$&').replace(/\u0000/g, '\\0');
}


function InjectScript(filepath, callback) {
	if (filepath) {
		var fileref = document.createElement('script');
		var done = false;
		var head = document.getElementsByTagName("head")[0];

		if(callback != undefined){
			fileref.onload = fileref.onreadystatechange = function () {
				if (!done && (!this.readyState || this.readyState === "loaded" || this.readyState === "complete")) {
					done = true;

					callback();

					// Handle memory leak in IE
					fileref.onload = fileref.onreadystatechange = null;
					if (head && fileref.parentNode) {
						head.removeChild(fileref);
					}
				}
			};
		}

		fileref.setAttribute("type", "text/javascript");
		fileref.setAttribute("src", filepath);

		head.appendChild(fileref);
	}
}

function sortByKey(array, key) {
	return array.sort(function(a, b) {
		var x = a[key]; var y = b[key];
		return ((x < y) ? -1 : ((x > y) ? 1 : 0));
	});
}

function GetAdditionalUrlParams(){
	var result = "";
	var sPageURL = window.location.search.substring(1);
	var sURLVariables = sPageURL.split('&');
	for (var i = 0; i < sURLVariables.length; i++) {
		var sParameterName = sURLVariables[i].split('=');
		if (sParameterName[0] != "conf") {
			result += "&" + sParameterName[0] + "=" + sParameterName[1];
		}
	}
	return result;
}

function GetURLParameter(sParam) {
	var sPageURL = window.location.search.substring(1);
	var sURLVariables = sPageURL.split('&');
	for (var i = 0; i < sURLVariables.length; i++) {
		var sParameterName = sURLVariables[i].split('=');
		if (sParameterName[0] == sParam) {
			return sParameterName[1];
		}
	}
}

var Hbar = {};

Hbar.display = function (container_id, series_key) {

	// clean out the div in case there is an old one in there
	$("#"+container_id+ " svg").remove();

	var data = JSON.parse($("#"+container_id).attr("data"));
	var coldefs = JSON.parse($("#"+container_id).attr("coldefs"));
	var limit = $("#"+container_id).attr("limit");
	var colors = d3.scale.category20();

	var colors=[];
	colors[1] = '#aec7e8';
	colors[2] = '#ff7f0e';
	colors[3] = '#ffbb78';

	if (series_key==undefined) {
		series_key = 1;
	}


	$("#"+container_id+" .seriebuttons button").removeClass("active");
	$("#"+container_id+" .seriebutton"+series_key).addClass("active");

	if (limit == 10) {
		var rowh = 28;
	} else {
		var rowh = 22;
	}

	// set up a drawing area
	var margin = {top: 60, right: 10, bottom: 10, left: 70},
		//width = 600 - margin.left - margin.right,
		width = parseInt(d3.select("#"+container_id).style('width')) - margin.left - margin.right,
		height = (limit*rowh) - margin.top - margin.bottom;

	var x = d3.scale.linear()
		.range([0, width]);

	var y = d3.scale.ordinal()
		.rangeRoundBands([0, height], .1);


	var svg = d3.select("#"+container_id).append("svg")
		.attr("width", width + margin.left + margin.right)
		//.attr("width", "100%")
		.attr("height", height + margin.top + margin.bottom)
		.append("g")
		.attr("transform", "translate(" + margin.left + "," + margin.top + ")");

	var legendHolder = svg.append("svg")
		.attr("class","legendHolder")
		.attr("width", width + margin.right)
		.attr("height","30")
		.attr("y","-55");

	var textLabel = svg.append("foreignObject")
		.attr("y", "-38")
		.attr("x", "-" + (margin.left))
		.attr("width", 60)
		.attr("height", 40)
		.append("xhtml:span")
		.attr('style','color: grey; position: absolute; right: 0; bottom: 0; text-align: right;')
		.attr("class", "textLabel");

	// get the max value form the selected serie
	var max = d3.max(data, function(d) { return d[series_key]; });

	// Set the scale domain.
	x.domain([0, max] );
	y.domain(d3.range(data.length));

	// add a background rectange so we can always color and hover over the entire row
	svg.selectAll(".back")
		.data(data)
		.enter().append('rect')
		.attr('class', 'back')
		.attr("y", function(d, i) { return y(i); })
		.attr('height', y.rangeBand())
		.attr('width', width)
		.attr("style", "background: #e0e0e0;")
		.attr("title", function(d) {
			r = d[0].split("##");
			return r[1];
		})

	var col_displayed = [];
	var col_layers = 0;
	// for each column of data, we may want to draw a bar
	$.each(coldefs, function(k,v) {
		//console.log(v.Label);
		col_displayed[k] = {
			displayed : false,
			layer : 0
		};

		// series key for now, so we are only plolting one serie at a time
		// if (k!=0 && k==series_key) {
		if (k != 0) {

			// add the value rectangle, which is the bar representing the relative size
			svg.selectAll(".value"+k)
				.data(data)
				.enter().append("rect")
				.attr("class", "value")
				.attr("col", k)
				.attr("y", function(d, i) { return y(i); })
				.attr("rx", 2)
				.attr("ry", 2)
				.attr("width", 0)
				//.style("fill", colors(v.name))
				.style("fill", colors[k])
				.attr("height", y.rangeBand())
				.attr("title", function(d) {
					r = d[0].split("##");
					return r[1];
				});



			// add the values along the left side
			svg.selectAll(".txtval"+k)
				.data(data)
				.enter().append("text")
				.attr("class", "txtval")
				.attr("col", k)
				.attr("x", -10)
				.attr("y", function(d, i) { return y(i) + y.rangeBand() / 2; })
				.attr("dy", ".35em")
				.style("text-anchor", "end")
				.style("display", "none")
				.text(function(d) {
					return d3.format(",d")(Math.round(d[k]));
				});

		}

	});

	// add the value label
	svg.selectAll(".name")
		.data(data)
		.enter().append("text")
		.attr("class", "name")
		.attr("x", 6)
		.attr("y", function(d, i) { return y(i) + y.rangeBand() / 2; })
		.attr("dx", -3)
		.attr("dy", ".35em")
		//.attr("style", "font-size:" + y.rangeBand()/1.7 + "px")
		.text(function(d) {
			r = d[0].split("##");
			return r[0];
		});

	// add the x axis
	svg.append("g")
		.attr("class", "x axis")
		.call(d3.svg.axis()
			.scale(x)
			.orient("top")
			.ticks(8)
			.tickSize(-height));

	// add the y-axis
	svg.append("g")
		.attr("class", "y axis")
		.append("line")
		.attr("y2", height);




	var i = 0;
	$.each(coldefs, function(k,v) {
		if (k != 0) {
			var legend = legendHolder.append("g")
				.attr("class", "legend")
				.style("float", "right")
				.attr("transform", function () {
						return "translate("+ (i * 100) +",0)";
					}
				);

			legend.append("circle")
				.attr("cx", 410)
				.attr("cy", 8)
				.attr("r", 5)
				.attr("col", k)
				.attr("fill", "white")
				//.attr("stroke", colors(v.name))
				.attr("stroke", colors[k])
				.attr("stroke-width", 2)
				.on("click", function () {
						update(k);
					}
				);
			legend.append("text")
				.attr("dx", 420)
				.attr("dy", "1em")
				.style("text-anchor", "start")
				.text(v.name)
				.on("click", function () {
						update(k);
					}
				);
			i++;
		}
	});

	var rescale = function(){
		var max = 0;
		var max_col = 0;
		$.each(coldefs, function(k,v) {
			if (k!=0 && col_displayed[k].displayed === true){
				m = d3.max(data, function(d) { return d[k]; });
				if (m > max) {
					max = m;
					max_col = k;
				}
			}
		});

		if(max !=0){
			x.domain([0, d3.max(data, function(d) { return d[max_col]; })]);

			svg.select(".x.axis") // change the x axis
				.call(d3.svg.axis()
					.scale(x)
					.orient("top")
					.ticks(8)
					.tickSize(-height));

			$.each(coldefs, function(k,v) {
				if (k!=0 && col_displayed[k].displayed === true){
					svg.selectAll(".value[col='"+k+"']")
						.transition()
						.attr("width",function(d) { return x(d[k]); })
						.duration(1000)
						.delay(100)
						.ease("bounce");
				}
			});
		}




	}

	var update = function(series_key) {



// get the max value of all the series
		$.each(coldefs, function(k,v) {
			//console.log(v.Label);

			// series key for now, so we are only plolting one serie at a time
			if (k!=0 && k==series_key) {
				// add the value rectangle, which is the bar representing the relative size

				if (col_displayed[k].displayed === true){
					svg.selectAll(".value[col='"+k+"']")
						.transition()
						.attr("width", 0)
						.duration(1000)
						.delay(100)
						.ease("elastic");


					if(col_displayed[k].layer == col_layers){
						var highest_layer = 0;
						var target = 0;
						var name = "";
						$.each(col_displayed, function (a,b) {
							if (k!= a && b.layer > highest_layer) {
								highest_layer = b.layer;
								target = a;
								name = coldefs[a].name;
							}
						});
						if (target != 0){
							svg.selectAll(".txtval[col='"+target+"']").style("display","block");
						}

						svg.selectAll(".textLabel").html(name);


					}else{
						$.each(col_displayed, function (a,b) {
							if (k!= a) {
								if (col_displayed[k].layer < b.layer){
									col_displayed[a].layer --;
								}
							}
						});
					}


					svg.selectAll(".txtval[col='"+k+"']").style("display","none");
					svg.selectAll("circle[col='"+k+"']").attr("fill", "white");
					col_displayed[k].displayed = false;
					col_displayed[k].layer = 0;
					col_layers --;


					// check if there are other layers above me and lower them by 1


				} else {
					svg.selectAll(".value[col='"+k+"']")
						.transition()
						.attr("width",function(d) { return x(d[k]); })
						.duration(1000)
						.delay(100)
						.ease("bounce");
					svg.selectAll(".textLabel").html(v.name);
					svg.selectAll(".txtval").style("display","none");
					svg.selectAll(".txtval[col='"+k+"']").style("display","block");
					//svg.selectAll("circle[col='"+k+"']").attr("fill", colors(v.name));
					svg.selectAll("circle[col='"+k+"']").attr("fill", colors[k]);


					col_displayed[k].displayed = true;
					col_layers ++;
					col_displayed[k].layer = col_layers;


				}
				rescale();
			}

		});

	}
	update(series_key);
}

var lg = {};

lg.Pie = function(container_id, series_key) {

	// clean out the div in case there is an old one in there
	$("#"+container_id+ " svg").remove();

	if (series_key==undefined) {
		series_key = 1;
	}

	var data = JSON.parse($("#"+container_id).attr("data"));

	var piedata = [];
	$.each(data, function(k,v) {
		piedata[k] = { "label": v[0], "value": v[series_key]  };
	});


	var coldefs = JSON.parse($("#"+container_id).attr("coldefs"));
	var limit = $("#"+container_id).attr("limit");

	var margin = {top: 0, right: 0, bottom: 0, left: 0};

	var	width = parseInt(d3.select("#"+container_id).style('width')) - margin.left - margin.right;

	var height = 300;
	if (limit > 10) {
		height += (limit-10)*14;
	}

	if(width <= 600){
		height = 220;
	}


	var svg = d3.select("#"+container_id).append("svg")
		.attr("width", width + margin.left + margin.right)
		//.attr("width", "100%")
		.attr("height", height + margin.top + margin.bottom)

	//Regular pie chart example
	nv.addGraph(function() {
		var chart = nv.models.pieChart()
			.x(function(d) { return d.label })
			.y(function(d) { return d.value })
			.width(width)
			.height(height)
			.showLabels(true)
			.valueFormat(d3.format(','))
			.labelThreshold(.02)  //Configure the minimum slice size for labels to show up
			.labelType("percent")
			.legendPosition("right");


		svg.datum(piedata)
			.transition().duration(350)
			.call(chart);

		nv.utils.windowResize(function() { chart.update() });


		return chart;
	});

}

lg.FunnelChart = function(container_id){

	$("#"+container_id).empty();

	var data = JSON.parse($("#"+container_id).attr("data-chart"));

	var options = {
		chart: {
			bottomPinch: 2,
			bottomWidth: 1 / 2,
			animate: 200,
			curve: {
				enabled: true,
			}
		},
		block : {
			dynamicHeight: true,
			highlight: true,
			minHeight: 10
		},
		events: {
			click: {
				block: function(d) {
					console.log(d);
				},
			},
		},
		label: {
			format: '',
		}
	};

	var chart = new D3Funnel("#" + container_id);
	chart.draw(data, options);
}

lg.LineChart = function(container_id){
	$("#"+container_id+ " svg").remove();

	var data = JSON.parse($("#"+container_id).attr("data-chart"));
	var chartoptions = JSON.parse($("#"+container_id).attr("data-chartoptions"));

	var xticks = JSON.parse($("#"+container_id).attr("data-ticks"));

	var coldefs = JSON.parse($("#"+container_id).attr("data-coldefs"));

	// make the chart bigger of we need to show the legend and have lots of labels
	if (chartoptions.showlegend == true && coldefs.length > 5) {
		height = 200 + (coldefs.length * 5);
	} else {
		height = 200;
	}

	var svg = d3.select("#"+container_id).append("svg")
		.attr("width", "100%")
		.attr("height", height+"px")
		.attr("class", "d3graph")


	var chart;

	var yformat = ",.0f";
	if(chartoptions.yFormat != undefined){
		yformat = chartoptions.yFormat;
	}

	nv.addGraph(function() {
		var chart = nv.models.lineChart()
			.margin({left: 65, right: 40})  //Adjust chart margins to give the x-axis some breathing room.
			.useInteractiveGuideline(true)  //We want nice looking tooltips and a guideline!
			.showLegend(chartoptions.showlegend)       //Show the legend, allowing users to turn on/off line series.
			.showYAxis(true)        //Show the y-axis
			.showXAxis(true)        //Show the x-axis
			.height(height)
			.x(function(d) { return d[0] })
			.y(function(d) { return d[1] })
			.color(d3.scale.category20().range())

		;

		chart.xAxis
			.tickFormat(function(d) {
				return xticks[d];
			});

		chart.yAxis
			.tickFormat(d3.format(yformat));

		svg.datum(data)         //Populate the <svg> element with chart data...
			.call(chart);          //Finally, render the chart!


		//Update the chart when window resizes.
		nv.utils.windowResize(function() { chart.update() });

		return chart;
	});
}
lg.BarChart = function(container_id, stacked){
	$("#"+container_id+ " svg").remove();

	var data = JSON.parse($("#"+container_id).attr("data-chart"));

	if (typeof $("#"+container_id).attr("data-colors") !== typeof undefined && $("#"+container_id).attr("data-colors") !== false) {
		var colors = JSON.parse($("#"+container_id).attr("data-colors"));
	} else {
		var colors = d3.scale.category20().range();
	}

	var xticks = JSON.parse($("#"+container_id).attr("data-ticks"));

	if (typeof $("#"+container_id).attr("data-controlLabels") !== typeof undefined && $("#"+container_id).attr("data-controlLabels") !== false) {
		var controlLabels = JSON.parse($("#"+container_id).attr("data-controlLabels"));
	} else {
		var controlLabels = false;
	}

	var chartoptions = JSON.parse($("#"+container_id).attr("data-chartoptions"));

	var height = 200;

	if (chartoptions.reduceXTicks !== undefined) {
		var reduceXTicks = chartoptions.reduceXTicks;
		height = height * 1.2;
	} else {
		var reduceXTicks = true;
	}

	var svg = d3.select("#"+container_id).append("svg")
		.attr("width", "100%")
		.attr("height", height+"px")
		.attr("class", "d3graph")

	// the stacked setting doubles as the switch to show the controls ...
	var showControls = stacked;

	var chart;

	nv.addGraph(function() {
		chart = nv.models.multiBarChart()
			.color(colors)
			.duration(300)
			.height(height)
			.margin({left: 65, right: 40})
			.stacked(stacked)
			.showControls(showControls)
			.controlLabels(controlLabels)
			.reduceXTicks(reduceXTicks)
		;

		chart.xAxis
			.tickFormat(function(d) {
				return xticks[d];
			});

		chart.yAxis
			.tickFormat(d3.format(',2f'));

		if (reduceXTicks != true) {
			chart.xAxis.rotateLabels(-45);
		}

		svg.datum(data)
			.call(chart);

		nv.utils.windowResize(chart.update);

		return chart;
	});
}

lg.BarLineChart = function(container_id, multi_axis) {

	if (multi_axis==undefined) {
		lg.MultiChart(container_id);
	}

	$("#"+container_id+ " svg").remove();

	var data = JSON.parse($("#"+container_id).attr("data"));

	var svg = d3.select("#"+container_id).append("svg")
		.attr("width", "100%")
		.attr("height", "200px")
		.attr("class", "d3graph")

	var chart;
	nv.addGraph(function() {
		chart = nv.models.linePlusBarChart()
			.height(200)
			.legendRightAxisHint(' [Using Right Axis]')
			.color(d3.scale.category20().range())
		;

		chart.xAxis.tickFormat(function(d) {
			var da = new Date(d);
			if(isNaN( da.getTime() )){ return d; }
			return d3.time.format('%x')(da);
		})
			.showMaxMin(false);

		chart.x2Axis.tickFormat(function(d) {
			return d3.time.format('%x')(new Date(d))
		}).showMaxMin(false);

		svg.datum(data)
			.transition().duration(500).call(chart);

		nv.utils.windowResize(chart.update);

		chart.dispatch.on('stateChange', function(e) { nv.log('New State:', JSON.stringify(e)); });

		return chart;
	});

}

lg.MultiChart = function(container_id){

	$("#"+container_id+ " svg").remove();

	var data = JSON.parse($("#"+container_id).attr("data-chart"));
	var max = $("#"+container_id).attr("data-max");
	var chartoptions = JSON.parse($("#"+container_id).attr("data-chartoptions"));

	if (typeof $("#"+container_id).attr("data-colors") !== typeof undefined && $("#"+container_id).attr("data-colors") !== false) {
		var colors = JSON.parse($("#"+container_id).attr("data-colors"));
	} else {
		var colors = d3.scale.category10().range();
	}

	var xticks = JSON.parse($("#"+container_id).attr("data-ticks"));

	var svg = d3.select("#"+container_id).append("svg")
		.attr("width", "100%")
		.attr("height", "200px")
		.attr("class", "d3graph")

	var chart;

	var yformat = ",2f";
	var y2format = ",.2f";
	if(chartoptions.yFormat != undefined){
		yformat = chartoptions.yFormat;
	}
	if(chartoptions.y2Format != undefined){
		y2format = chartoptions.y2Format;
	}

	nv.addGraph(function() {
		var chart = nv.models.multiChart()
			.margin({top: 30, right: 40, bottom: 50, left: 65})
			.color(colors)
			.height(230)
			.showLegend(true)
		;


		chart.yAxis1
			.tickFormat(d3.format(yformat))
			.axisLabel(chartoptions.yAxis1label);
		chart.yAxis2.tickFormat(d3.format(y2format));

		chart.xAxis.tickFormat(function(d) {
			return xticks[d];
		})
			.showMaxMin(true);

		chart.tooltip.headerFormatter(function(d, i) {
			return chart.xAxis.tickFormat()(d, i);
		})
			.valueFormatter(function(d, i) {
				return chart.yAxis.tickFormat()(d, i);
			})

		chart.yDomain1([0, max]);

		svg.datum(data)
			.transition().duration(500).call(chart);

		nv.utils.windowResize(function() { chart.update() });
		return chart;
	});

}

lg.StackedAreaChart = function(container_id){

	$("#"+container_id+ " svg").remove();

	var data = JSON.parse($("#"+container_id).attr("data-chart"));
	var chartoptions = JSON.parse($("#"+container_id).attr("data-chartoptions"));
	var coldefs = JSON.parse($("#"+container_id).attr("data-coldefs"));
	var xticks = JSON.parse($("#"+container_id).attr("data-ticks"));
	var controlLabels = JSON.parse($("#"+container_id).attr("data-controlLabels"));

	// make the chart bigger of we need to show the legend and have lots of labels
	var height;
	if (chartoptions.showlegend == true && coldefs.length > 4) {
		// if (chartoptions.stacked_showcontrols ==true) {
		// 	h = 14;
		// } else {
		// 	h = 7;
		// }
		if (coldefs.length > 20) {
			chartoptions.showlegend = false;
			height = 200;
		} else {
			height = 200 + (coldefs.length * 6);
		}

	} else {
		height = 200;
	}

	var svg = d3.select("#"+container_id).append("svg")
		.attr("width", "100%")
		.attr("height", height+"px")
		.attr("class", "d3graph")

	var chart;

	var yformat = ",2f";
	if(chartoptions.yFormat != undefined){
		yformat = chartoptions.yFormat;
	}

	nv.addGraph(function() {
		var chart = nv.models.stackedAreaChart()
			.margin({right: 40, left: 65 })
			.height(height)
			.x(function(d) { return d[0] })   //We can modify the data accessor functions...
			.y(function(d) { return d[1] })   //...in case your data is formatted differently.
			.useInteractiveGuideline(true)    //Tooltips which show all data points. Very nice!
			.showControls(chartoptions.stacked_showcontrols)       //Allow user to choose 'Stacked', 'Stream', 'Expanded' mode.
			.clipEdge(true)
			.style(chartoptions.stacked_graph_style)
			.color(d3.scale.category20().range())
			.showLegend(chartoptions.showlegend)
			.controlLabels(controlLabels)
		;

		chart.xAxis
			.tickFormat(function(d) {
				return xticks[d];
			});

		chart.yAxis
			.tickFormat(d3.format(yformat));

		chart.interactiveLayer.tooltip.headerFormatter(function(d) { return d; });

		svg.datum(data)
			.call(chart);

		nv.utils.windowResize(chart.update);


		return chart;
	});
}


lg.HorizontalMultiBar = function(container_id){

	$("#"+container_id+ " svg").remove();

	var data = JSON.parse($("#"+container_id).attr("data-chart"));
	var chartoptions = JSON.parse($("#"+container_id).attr("data-chartoptions"));
	var coldefs = JSON.parse($("#"+container_id).attr("data-coldefs"));
	//var xticks = JSON.parse($("#"+container_id).attr("data-ticks"));

	// make the chart bigger of we need to show the legend and have lots of labels
	var height = 1200;


	var svg = d3.select("#"+container_id).append("svg")
		.attr("width", "100%")
		.attr("height", height+"px")
		.attr("class", "d3graph")

	var chart;

	var yformat = ",2f";
	if(chartoptions.yFormat != undefined){
		yformat = chartoptions.yFormat;
	}


	nv.addGraph(function() {
		var chart = nv.models.multiBarHorizontalChart()
			.x(function(d) { return d.label })
			.y(function(d) { return d.value })
			.margin({top: 30, right: 20, bottom: 50, left: 175})
			.showValues(true)           //Show bar value next to each bar.
			//.tooltips(true)             //Show tooltips on hover.
			//.transitionDuration(350)
			.showControls(true);        //Allow user to switch between "Grouped" and "Stacked" mode.

		chart.yAxis
			.tickFormat(d3.format(',.2f'));

		svg.datum(data)
			.call(chart);

		nv.utils.windowResize(chart.update);

		return chart;
	});
}

lg.Sunburst = function(container_id){

	$("#"+container_id+ " svg").remove();

	var data = JSON.parse($("#"+container_id).attr("data-chart"));
	var chartoptions = JSON.parse($("#"+container_id).attr("data-chartoptions"));
	var coldefs = JSON.parse($("#"+container_id).attr("data-coldefs"));

	if (typeof $("#"+container_id).attr("data-colors") !== typeof undefined && $("#"+container_id).attr("data-colors") !== false) {
		var colors = JSON.parse($("#"+container_id).attr("data-colors"));
		if (colors.d3cat!=undefined) {

			colors = eval(colors.d3cat);
		}
	} else {
		var colors = d3.scale.category20c().range();
	}

	var height = 350;

	var svg = d3.select("#"+container_id).append("svg")
		.attr("width", "100%")
		.attr("height", height+"px")
		.attr("class", "d3graph")

	var chart;

	var yformat = ",2f";
	if(chartoptions.yFormat != undefined){
		yformat = chartoptions.yFormat;
	}

	nv.addGraph(function() {
		chart = nv.models.sunburstChart();
		chart.color(colors);
		chart.mode("size");
		chart.labelThreshold(3);
		svg.datum(data)
			.call(chart);

		nv.utils.windowResize(chart.update);
		return chart;
	});
}

// UPDATED -> NOW WORKS WITH jQuery 1.3.1
// usage $("*").listHandlers("*", console.info);
$.fn.listHandlers = function(events, outputFunction) {
	return this.each(function(i){
		var elem = this,
			dEvents = $(this).data('events');
		if (!dEvents) {return;}
		$.each(dEvents, function(name, handler){
			if((new RegExp('^(' + (events === '*' ? '.+' : events.replace(',','|').replace(/^on/i,'')) + ')$' ,'i')).test(name)) {
				$.each(handler, function(i,handler){
					outputFunction(elem, 'n' + i + ': [' + name + '] : ' + handler );
				});
			}
		});
	});
};

function unixslash(str,field) {
	newstr = str.replace(/\\/g,'/');
	if (newstr!=str) {
		document.getElementById(field).value = newstr;
	}
	return newstr;
}


function Get_Cookie(name) {
	var start = document.cookie.indexOf(name+"=");
	var len = start+name.length+1;
	if ((!start) && (name != document.cookie.substring(0,name.length))) return null;
	if (start == -1) return null;
	var end = document.cookie.indexOf(";",len);
	if (end == -1) end = document.cookie.length;
	return unescape(document.cookie.substring(len,end));
}

function Set_Cookie(name,value,expires,path,domain,secure) {
	var cookieString = name + "=" +escape(value) +
		( (expires) ? ";expires=" + expires.toGMTString() : "") +
		( (path) ? ";path=" + path : "") +
		( (domain) ? ";domain=" + domain : "") +
		( (secure) ? ";secure" : "");
	document.cookie = cookieString;
}

function Delete_Cookie(name,path,domain) {
	if (Get_Cookie(name)) document.cookie = name + "=" +
		( (path) ? ";path=" + path : "") +
		( (domain) ? ";domain=" + domain : "") +
		";expires=Thu, 01-Jan-70 00:00:01 GMT";
}

function FormObjectToArray(object){
	var data = object.serializeArray();
	var result = {};
	$.each(data, function(k, v){
		/* set the data in the array as k => v */
		result[v.name] = v.value;
	});
	return result;
}

function ValidateEmail(email) {
	var re = /^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
	return re.test(email);
}

function checkPassword(str){
	// at least one number, one lowercase and one uppercase letter
	// at least six characters
	var re = /(?=.*\d)(?=.*[a-z])(?=.*[A-Z]).{6,}/;
	return re.test(str);
}

function ReplaceDataTablesSearch(target , placeholder){
	var bFilter = $(target +' .dataTables_filter');

	bFilter.find('label').html( bFilter.find('input').clone(true) );
	bFilter.find('input').attr("placeholder", placeholder );
	$(target +' .dataTables_length').parents("div").first().css("float", "right");
}

// Don't allow special characters to be entered into form.
function noSpecialChars(e)
{
	var keynum
	var iChars = " !@#$%^&*()+=-[]\\\';,./{}|\":<>?"; // Invalid characters.

	if	(window.event) { keynum = e.keyCode; } // IE
	else if (e.which) { keynum = e.which; } // Netscape/Firefox/Opera

	return (iChars.indexOf(String.fromCharCode(keynum)) == -1);
}

function srrowOverEffect(object) {
	if (object.className == 'profilerow') object.className = 'profilerowhighlight';
}

function srrowOutEffect(object) {
	if (object.className == 'profilerowhighlight') object.className = 'profilerow';
}

function QBuilderHelpForms(field, passevent, passvalue, passtype) {
	if (field=="" || field=="bytes") {
		return;
	}
	popupMenu(passevent, passvalue, passtype);
}

function toFunnelForm(field,val,oldval) {
	document.editform.elements[field].value=oldval+val;
	hide_popup_menu();
}

/*
 * SIDEBAR MENU
 * ------------
 * This is a custom plugin for the sidebar menu. It provides a tree view.
 *
 * Usage:
 * $(".sidebar).tree();
 *
 * Note: This plugin does not accept any options. Instead, it only requires a class
 *       added to the element that contains a sub-menu.
 *
 * When used with the sidebar, for example, it would look something like this:
 * <ul class='sidebar-menu'>
 *      <li class="treeview active">
 *          <a href="#>Menu</a>
 *          <ul class='treeview-menu'>
 *              <li class='active'><a href=#>Level 1</a></li>
 *          </ul>
 *      </li>
 * </ul>
 *
 * Add .active class to <li> elements if you want the menu to be open automatically
 * on page load. See above for an example.
 */
(function($) {
	"use strict";

	$.fn.tree = function() {

		return this.each(function() {
			var btn = $(this).children("a").first();
			var menu = $(this).children(".treeview-menu").first();
			//var isActive = $(this).hasClass('active');

			//initialize already active menus
			// if (isActive) {
			//     menu.show();
			//     btn.children(".fa-angle-left").first().removeClass("fa-angle-left").addClass("fa-angle-down");
			// }
			//Slide open or close the menu on link click
			btn.click(function(e) {
				e.preventDefault();
				e.stopPropagation();

				if (btn.parent("li").hasClass('active')) {
					//Slide up to close menu
					menu.slideUp();
					//isActive = false;
					btn.children(".fa-angle-down").first().removeClass("fa-angle-down").addClass("fa-angle-left");
					btn.parent("li").removeClass("active");
				} else {
					//Slide down to open menu
					$(".treeview-menu").slideUp(150);
					$(".treeview.active .fa-angle-down").removeClass("fa-angle-down").addClass("fa-angle-left");
					$(".treeview.active").removeClass("active");

					menu.slideDown(150);
					//isActive = true;
					btn.children(".fa-angle-left").first().removeClass("fa-angle-left").addClass("fa-angle-down");
					btn.parent("li").addClass("active");
				}
			});

			/* Add margins to submenu elements to give it a tree look */
			menu.find("li > a").each(function() {
				//     var pad = parseInt($(this).css("margin-left")) + 10;

				//     $(this).css({"margin-left": pad + "px"});
				$(this).css({"margin-left": 10 + "px"});
			});

		});

	};


}(jQuery));

function base64_encode(data) {
	//  discuss at: http://phpjs.org/functions/base64_encode/
	// original by: Tyler Akins (http://rumkin.com)
	// improved by: Bayron Guevara
	// improved by: Thunder.m
	// improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
	// improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
	// improved by: Rafał Kukawski (http://kukawski.pl)
	// bugfixed by: Pellentesque Malesuada
	//   example 1: base64_encode('Kevin van Zonneveld');
	//   returns 1: 'S2V2aW4gdmFuIFpvbm5ldmVsZA=='
	//   example 2: base64_encode('a');
	//   returns 2: 'YQ=='

	var b64 = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=';
	var o1, o2, o3, h1, h2, h3, h4, bits, i = 0,
		ac = 0,
		enc = '',
		tmp_arr = [];

	if (!data) {
		return data;
	}

	do { // pack three octets into four hexets
		o1 = data.charCodeAt(i++);
		o2 = data.charCodeAt(i++);
		o3 = data.charCodeAt(i++);

		bits = o1 << 16 | o2 << 8 | o3;

		h1 = bits >> 18 & 0x3f;
		h2 = bits >> 12 & 0x3f;
		h3 = bits >> 6 & 0x3f;
		h4 = bits & 0x3f;

		// use hexets to index into b64, and append result to encoded string
		tmp_arr[ac++] = b64.charAt(h1) + b64.charAt(h2) + b64.charAt(h3) + b64.charAt(h4);
	} while (i < data.length);

	enc = tmp_arr.join('');

	var r = data.length % 3;

	return (r ? enc.slice(0, r - 3) : enc) + '==='.slice(r || 3);
}



// this can be used to delay ajax calls
var delay = (function(){
	var timer = 0;
	return function(callback, ms){
		clearTimeout (timer);
		timer = setTimeout(callback, ms);
	};
})();

