b0y-101 Mini Shell


Current Path : E:/www3/chiangrai/wp-content/plugins/download-monitor/assets/js/reports/
File Upload :
Current File : E:/www3/chiangrai/wp-content/plugins/download-monitor/assets/js/reports/reports.js

/**
 * DLM Reports script
 */
jQuery(function ($) {

	// Let's initiate the reports.
	const reports = new DLM_Reports();

	// Set spinner so that users know there is something going on.
	dlmReportsInstance.setSpinner(jQuery('.total_downloads_chart-wrapper'));
	dlmReportsInstance.setSpinner(jQuery('#users_download_log'));
	dlmReportsInstance.setSpinner(jQuery('#total_downloads_table_wrapper2'));

	// Let's get the available Downloads
	dlmReportsInstance.fetchDownloadsCPT();
});

/**
 * DLM Reports class
 */
class DLM_Reports {
	dlmReportsStats = [];
	dlmUsersStats   = {
		logs : [],
		users: []
	};
	currentFilters  = [];
	tempDownloads   = null;
	templates       = {};
	totalDownloads  = 0;
	perPage         = dlmReportsPerPage;
	downloads = [];
	topDownloadsOrder = 'count';
	weekstart       = dlmWeekStart;

	/**
	 * The constructor for our class
	 */
	constructor() {
		dlmReportsInstance                = this;
		dlmReportsInstance.chartContainer = document.getElementById('total_downloads_chart');
		const ctx                         = dlmReportsInstance.chartContainer.getContext("2d");

		/**
		 * Gradient for the chart
		 */
		dlmReportsInstance.chartColors = {
			purple        : {
				default  : "rgba(149, 76, 233, 1)",
				threesome: "rgba(149, 76, 233, 0.75)",
				half     : "rgba(149, 76, 233, 0.5)",
				quarter  : "rgba(149, 76, 233, 0.5)",
				zero     : "rgba(149, 76, 233, 0.05)"
			}, blue       : {
				default  : "rgba(67, 56, 202, 1)",
				threesome: "rgba(67, 56, 202, 0.75)",
				half     : "rgba(67, 56, 202, 0.5)",
				quarter  : "rgba(67, 56, 202, 0.25)",
				zero     : "rgba(67, 56, 202, 0.05)"
			}, green      : {
				default  : "rgba(00, 255, 00, 1)",
				threesome: "rgba(00, 255, 00, 0.75)",
				half     : "rgba(00, 255, 00, 0.5)",
				quarter  : "rgba(00, 255, 00, 0.25)",
				zero     : "rgba(67, 56, 202, 0.05)",
			}, royalBlue  : {
				default  : "rgba(65, 105, 225, 1)",
				threesome: "rgba(65, 105, 225, 0.75)",
				half     : "rgba(65, 105, 225, 0.5)",
				quarter  : "rgba(65, 105, 225, 0.25)",
				zero     : "rgba(65, 105, 225, 0.05)",
			}, persianBlue: {
				default  : "rgba(28, 57, 187, 1)",
				threesome: "rgba(28, 57, 187, 0.75)",
				half     : "rgba(28, 57, 187, 0.5)",
				quarter  : "rgba(28, 57, 187, 0.25)",
				zero     : "rgba(28, 57, 187, 0.05)",
			}, darkCyan   : {
				default  : "rgba(0,129,167, 1)",
				threesome: "rgba(0,129,167, 0.75)",
				half     : "rgba(0,129,167, 0.5)",
				quarter  : "rgba(0,129,167, 0.25)",
				zero     : "rgba(0,129,167, 0.05)",
			}, strongCyan : {
				default  : "rgba(0, 175, 185, 1)",
				threesome: "rgba(0, 175, 185, 0.75)",
				half     : "rgba(0, 175, 185, 0.5)",
				quarter  : "rgba(0, 175, 185, 0.25)",
				zero     : "rgba(0, 175, 185, 0.05)",
			}
		};
		dlmReportsInstance.chartGradient = ctx.createLinearGradient(0, 25, 0, 300);
		dlmReportsInstance.chartGradient.addColorStop(0, dlmReportsInstance.chartColors.darkCyan.half);
		dlmReportsInstance.chartGradient.addColorStop(0.45, dlmReportsInstance.chartColors.darkCyan.quarter);
		dlmReportsInstance.chartGradient.addColorStop(1, dlmReportsInstance.chartColors.darkCyan.zero);
		dlmReportsInstance.datePickerContainer = document.getElementById('dlm-date-range-picker');
		dlmReportsInstance.dataSets            = [];
		let date                               = new Date();
		dlmReportsInstance.dates               = {
			downloads: {
				start_date: new Date(date.setMonth(date.getMonth() - 1)),
				end_date  : new Date()
			}
		};
		dlmReportsInstance.chartDataObject     = {};
	}

	/**
	 * Fetch our needed data from REST API. This includes the init() function because we need our data to be present and the moment of the initialization
	 *
	 * @param offset Offset used for database query
	 * @param limit Limit used for database query
	 */
	async fetchReportsData(offset = 0, limit = dlmPHPinfo['retrieved_chart_stats']) {

		const pageWrapper          = jQuery('div[data-id="general_info"]');

		// Let's see if these are pretty permalinks or plain
		let fetchingLink = dlmDownloadReportsAPI + '&offset=' + offset + '&limit=' + limit;
		// Fetch our data
		const fetchedDownloadsData = await fetch(fetchingLink);

		if (!fetchedDownloadsData.ok) {
			const errorText     = document.createElement('div');
			errorText.className = "dlm-loading-data";

			const t1                                    = document.createTextNode('Seems like we bumped into an error! '),
				  t2                                    = document.createTextNode('Data fetching returned a status text of : ' + fetchedData.statusText),
				  p1 = document.createElement('h1'), p2 = document.createElement('h3');
			p1.appendChild(t1);
			p2.appendChild(t2);
			errorText.appendChild(p1);
			errorText.appendChild(p2);
			pageWrapper.find('.dlm-loading-data').remove();
			pageWrapper.append(errorText);
			throw new Error('Something went wrong! Reports response did not come OK - ' + fetchedData.statusText);
		}

		dlmReportsInstance.mostDownloaded  = false;
		dlmReportsInstance.stats           = false;
		dlmReportsInstance.chartType       = 'day';

		let response                          = await fetchedDownloadsData.json();
		dlmReportsInstance.dlmReportsStats = dlmReportsInstance.dlmReportsStats.concat(response.stats);

		if (true === response.done) {
			// Set the "all time" reports if URL has dlm_time. Used when coming from Dashboard widget
			if (window.location.href.indexOf('dlm_time') > 0) {
				dlmReportsInstance.dates.downloads.start_date = (Object.keys(dlmReportsInstance.dlmReportsStats).length > 0) ? new Date(dlmReportsInstance.dlmReportsStats[0].date) : new Date();
				dlmReportsInstance.dates.downloads.end_date   = new Date();
				jQuery('#dlm-date-range-picker .date-range-info').html(dlmReportsInstance.dates.downloads.start_date.toLocaleDateString(undefined, {
					year: 'numeric', month: 'short', day: '2-digit'
				}) + ' - ' + dlmReportsInstance.dates.downloads.end_date.toLocaleDateString(undefined, {
					year: 'numeric', month: 'short', day: '2-digit'
				}));
			}
			dlmReportsInstance.createDataOnDate(dlmReportsInstance.dates.downloads.start_date, dlmReportsInstance.dates.downloads.end_date);

			dlmReportsInstance.datePicker = {
				opened: false
			};

			jQuery(document).trigger('dlm_downloads_report_fetched', [
				dlmReportsInstance,
				dlmReportsInstance.dlmReportsStats
			]);

			dlmReportsInstance.stopSpinner(jQuery('.total_downloads_chart-wrapper'));
			dlmReportsInstance.init();
		} else {
			dlmReportsInstance.fetchReportsData(response.offset);
		}
	}

	/**
	 * The request for users reports
	 *
	 * @param offset
	 * @param limit
	 * @returns {Promise<void>}
	 */
	async fetchUsersReportsData(offset = 0, limit = dlmPHPinfo['retrieved_rows']) {

		const wrapper         = jQuery('div[data-id="user_reports"]');

		// Let's see if these are pretty permalinks or plain
		let fetchingLink = dlmUserReportsAPI + '&offset=' + offset + '&limit=' + limit;

		const fetchedUserData = await fetch(fetchingLink);

		if (!fetchedUserData.ok) {
			throw new Error('Something went wrong! Reports response did not come OK - ' + fetchedUserData.statusText);
		}

		let response                          = await fetchedUserData.json();
		dlmReportsInstance.dlmUsersStats.logs = dlmReportsInstance.dlmUsersStats.logs.concat(response.logs);

		if (true === response.done) {
			dlmReportsInstance.userDownloads = ('undefined' !== typeof dlmReportsInstance.dlmUsersStats.logs) ? JSON.parse(JSON.stringify(dlmReportsInstance.dlmUsersStats.logs)) : {};
			wrapper.find('.dlm-loading-data').remove();
			dlmReportsInstance.userReportsTab();
			dlmReportsInstance.setTopDownloads();
			dlmReportsInstance.stopSpinner(jQuery('#total_downloads_table_wrapper2'));
		} else {
			dlmReportsInstance.fetchUsersReportsData(response.offset);
		}
	}

	/**
	 * The request for user data
	 * @returns {Promise<void>}
	 */
	async fetchUserData( offset = 0, limit = dlmPHPinfo['retrieved_user_data'] ) {

		// Let's see if these are pretty permalinks or plain
		let fetchingLink = dlmUserDataAPI + '&offset=' + offset + '&limit=' + limit;

		const fetchedUserData = await fetch(fetchingLink);

		if (!fetchedUserData.ok) {
			throw new Error('Something went wrong! Reports response did not come OK - ' + fetchedUserData.statusText);
		}

		let response                           = await fetchedUserData.json();
		dlmReportsInstance.dlmUsersStats.users = dlmReportsInstance.dlmUsersStats.users.concat(response.logs);

		if (true === response.done) {

			// Get the data used for the chart. We get it so that the users are completed and we won't possibly overload the server
			dlmReportsInstance.fetchReportsData();

		} else {
			
			dlmReportsInstance.fetchUserData(response.offset);
		}


	}

	/**
	 * Init our methods
	 */
	init() {

		dlmReportsInstance.tabNagivation();
		dlmReportsInstance.overViewTab();
		dlmReportsInstance.togglePageSettings();

		dlmReportsInstance.fetchUsersReportsData();
		// Trigger action so others can hook into this
		jQuery(document).trigger('dlm_reports_init', [dlmReportsInstance]);
		dlmReportsInstance.eventsFunctions();
	}

	/**
	 * The overview functionality
	 */
	overViewTab() {

		dlmReportsInstance.dlmCreateChart(dlmReportsInstance.stats.chartStats, dlmReportsInstance.chartContainer);
		dlmReportsInstance.dlmDownloadsSummary();
		dlmReportsInstance.datePickerContainer.addEventListener('click', dlmReportsInstance.toggleDatepicker.bind(this));
		dlmReportsInstance.setTodayDownloads();
		dlmReportsInstance.handleTopDownloads();

		jQuery(document).on('click', 'body', function (event) {

			event.stopPropagation();

			if (jQuery(dlmReportsInstance.datePickerContainer).find('#dlm_date_range_picker').length > 0) {
				dlmReportsInstance.hideDatepicker(jQuery(dlmReportsInstance.datePickerContainer), {target: 'dlm-date-range-picker'});
			}
		});
	}

	/**
	 * The user reports functiuonality
	 */
	userReportsTab() {

		if (0 === Object.values(dlmReportsInstance.dlmUsersStats).length) {
			return;
		}
		dlmReportsInstance.logsDataByDate(dlmReportsInstance.dates.downloads.start_date, dlmReportsInstance.dates.downloads.end_date);
		dlmReportsInstance.handleUserDownloads();
		dlmReportsInstance.filterDownloads();
	}

	/**
	 * Get all dates in set intervals
	 * Used for chart data
	 *
	 * @param startDate
	 * @param endDate
	 * @returns {*[]}
	 */
	getDates(startDate, endDate) {

		const dates     = {};
		let currentDate = startDate;

		while (currentDate.getTime() < endDate.getTime()) {
			dates[this.createDateElement(currentDate)] = 0;
			currentDate                                = this.getNextDay(currentDate);
		}
		// Add another day to get the last day
		dates[this.createDateElement(currentDate)] = 0;
		currentDate                                = this.getNextDay(currentDate);

		return dates;
	}

	/**
	 * Get all months in set intervals
	 * Used for chart data
	 *
	 * @returns {*[]}
	 * @param days
	 */
	getMonths(days) {

		const dates = {};

		Object.keys(days).map(element => {

			let subString = element.substring(0, 7);

			if ('undefined' === typeof dates[subString]) {
				dates[subString] = 0;
			}
		});

		return dates;
	}

	/**
	 * Get all 2 months in set intervals
	 * Used for chart data
	 *
	 * @returns {*[]}
	 * @param days
	 */
	getDoubleMonths(days) {

		const dates = {}, firstDay = Object.keys(days)[0],
			  lastDay              = Object.keys(days)[Object.keys(days).length - 1];

		let i = 0, month = firstDay.substring(0, 7), lastMonth = lastDay.substring(0, 7);

		Object.keys(days).map(element => {

			let subString = element.substring(0, 7);

			if (month !== subString && lastMonth !== subString) {
				month = subString;
				i++;
			}

			if ('undefined' === typeof dates[subString] && 0 === i % 2) {
				dates[subString] = 0;
			}

		});

		return dates;
	}

	/**
	 * Get all 2 weeks in set intervals
	 * Used for chart data
	 *
	 * @returns {*[]}
	 * @param days
	 */
	getWeeks(days) {

		let dates = {};
		Object.keys(days).forEach(element => {
			let week;
			if (moment(element).date() > 15) {
				week = element.substring(0, 7) + '-15';
			} else {
				week = element.substring(0, 7) + '-01';
			}

			if ('undefined' === typeof dates[week]) {
				dates[week] = 0;
			}
		});

		return dates;
	}

	/**
	 * Get all weeks in set intervals
	 * Used for chart data
	 *
	 * @returns {*[]}
	 * @param days
	 */
	getWeek(days) {

		let dates   = {},
			lastDay = Object.keys(days)[Object.keys(days).length - 1],
			i       = 0;

		Object.keys(days).map(element => {
			if ('undefined' === typeof dates[element] && 0 === i % 7) {
				dates[element] = 0;
			}
			i++;
		});

		// Also add last date if not in array
		if ('undefined' === typeof dates[lastDay]) {
			dates[lastDay] = 0;
		}
		return dates;
	}

	/**
	 * Get all 2 months in set intervals
	 * Used for chart data
	 *
	 * @returns {*[]}
	 * @param days
	 */
	getDoubleDays(days) {

		let dates = {}, firstDay = Object.keys(days)[0],
			lastDay              = Object.keys(days)[Object.keys(days).length - 1];

		let i = 0;
		Object.keys(days).map(element => {

			if (firstDay !== element && lastDay !== element) {
				firstDay = element;
				i++;
			}

			if ('undefined' === typeof dates[element] && 0 === i % 2) {
				dates[element] = 0;
			}

		});

		return dates;
	}

	/**
	 * Get the next day
	 *
	 * @param currentDate
	 * @returns {Date}
	 */
	getNextDay(currentDate) {

		const date = new Date(currentDate);
		date.setDate(currentDate.getDate() + 1);
		return date;
	}

	/**
	 * Requires a Date object and resturns a string
	 * @param date
	 * @returns {string}
	 */
	createDateElement(date) {

		var MM = ((date.getMonth() + 1) < 10 ? '0' : '') + (date.getMonth() + 1);

		return date.getFullYear() + '-' + MM + '-' + ("0" + date.getDate()).slice(-2);
	}

	/**
	 * Get set dates by datepicker
	 *
	 * @param startDateInput
	 * @param endDateInput
	 * @returns {{endDate: *, startDate: *}}
	 */
	getSetDates(startDateInput, endDateInput) {

		let startDate, endDate;
		if ('undefined' !== typeof startDateInput && startDateInput) {

			startDate = dlmReportsInstance.createDateElement(new Date(startDateInput));
		} else {
			// If there are no startDateInput it means it is the first load, so get last 30 days.
			const lastMonth = new Date();
			lastMonth.setDate(lastMonth.getDate() - 30);
			startDate = dlmReportsInstance.createDateElement(lastMonth);

		}

		if ('undefined' !== typeof endDateInput && endDateInput) {
			let trueEnd = new Date(endDateInput);
			endDate     = dlmReportsInstance.createDateElement(trueEnd);

		} else {

			// If there is no endDateInput we'll set the endDate to tomorrow so that we can include today in our reports also.
			// Seems like this is how the datepicker works.
			const tomorrow = new Date();
			tomorrow.setDate(tomorrow.getDate() + 1);
			endDate = dlmReportsInstance.createDateElement(tomorrow);
		}
		return {startDate, endDate};
	}

	/**
	 * Filter data to send to chart based on user input start & end date
	 *
	 * @param startDateInput
	 * @param endDateInput
	 * @returns {*}
	 */
	createDataOnDate(startDateInput, endDateInput) {

		let {startDate, endDate} = {...dlmReportsInstance.getSetDates(startDateInput, endDateInput)}, dayDiff,
			monthDiff,
			yearDiff, chartDate, dlmDownloads;

		dlmReportsInstance.reportsData = ('undefined' !== typeof dlmReportsInstance.dlmReportsStats) ? JSON.parse(JSON.stringify(dlmReportsInstance.dlmReportsStats)) : {};
		monthDiff                      = moment(endDate, 'YYYY-MM-DD').month() - moment(startDate, 'YYYY-MM-DD').month();
		yearDiff                       = moment(endDate, 'YYYY-MM-DD').year() - moment(startDate, 'YYYY-MM-DD').year();
		dayDiff                        = moment(endDate).date() - moment(startDate).date();
		dlmReportsInstance.chartType   = 'day';

		if (yearDiff == 0 && monthDiff > -6 && monthDiff < 6) {
			if (monthDiff > 1 || monthDiff < -1) {
				if (2 === monthDiff) {
					dlmReportsInstance.chartType = 'week';
				} else {
					dlmReportsInstance.chartType = 'weeks';
				}
			} else {
				if (1 === monthDiff && (dayDiff > 8 || dayDiff > -14 || 0 === dayDiff)) {
					dlmReportsInstance.chartType = 'days';
				}
			}
		} else {
			if (yearDiff = 1 && monthDiff <= 0) {
				dlmReportsInstance.chartType = 'month';
			} else {
				dlmReportsInstance.chartType = 'months';
			}
		}

		// Get all dates from the startDate to the endDate
		let dayDownloads = dlmReportsInstance.getDates(new Date(startDate), new Date(endDate)),
			doubleDays, doubleMonthDownloads, weeksDownloads, weekDownloads, monthDownloads;

		// Let's initiate our dlmDownloads with something
		switch (dlmReportsInstance.chartType) {
			case 'months':
				// Get double selected months
				doubleMonthDownloads = dlmReportsInstance.getDoubleMonths(dayDownloads);
				dlmDownloads         = doubleMonthDownloads;
				break;
			case 'month':
				// Get selected months
				monthDownloads = dlmReportsInstance.getMonths(dayDownloads);
				dlmDownloads       = monthDownloads;
				break;
			case 'weeks':
				// Get selected dates in 2 weeks grouping
				weeksDownloads = dlmReportsInstance.getWeeks(dayDownloads);
				dlmDownloads   = weeksDownloads;
				break
			case 'week':
				// Get selected dates in 2 weeks grouping
				weekDownloads = dlmReportsInstance.getWeek(dayDownloads);
				dlmDownloads  = weekDownloads;
				break
			case 'days':
				// Get double days
				doubleDays   = dlmReportsInstance.getDoubleDays(dayDownloads);
				dlmDownloads = doubleDays;
				break
			case 'day':
				dlmDownloads = dayDownloads;
				break;
		}

		Object.values(dlmReportsInstance.reportsData).forEach((day, index) => {

			const downloads = JSON.parse(day.download_ids);

			if ('undefined' !== typeof dayDownloads[day.date]) {

				switch (dlmReportsInstance.chartType) {
					case 'months':
						chartDate      = day.date.substring(0, 7);
						let chartMonth = parseInt(day.date.substring(5, 7)),
							chartYear  = day.date.substring(0, 5),
							prevDateI  = (chartMonth - 1).length > 6 ? chartYear + (chartMonth - 1) : chartYear + '0' + (chartMonth - 1);

						Object.values(downloads).forEach((item, index) => {

							// If it does not exist we attach the downloads to the previous month
							if ('undefined' === typeof doubleMonthDownloads[chartDate]) {
								if ('undefined' !== typeof doubleMonthDownloads[prevDateI]) {
									doubleMonthDownloads[prevDateI] = doubleMonthDownloads[prevDateI] + item.downloads;
								}
							} else {
								doubleMonthDownloads[chartDate] = doubleMonthDownloads[chartDate] + item.downloads;
							}

						});

						dlmDownloads = doubleMonthDownloads;
						break;
					case 'month':

						chartDate = day.date.substring(0, 7);

						Object.values(downloads).forEach((item, index) => {
							monthDownloads[chartDate] = ('undefined' !== typeof monthDownloads[chartDate]) ? monthDownloads[chartDate] + item.downloads : item.downloads;
						});

						dlmDownloads = monthDownloads;
						break;
					case 'weeks':

						if (moment(day.date).date() > 15) {
							chartDate = day.date.substring(0, 7) + '-15';
						} else {
							chartDate = day.date.substring(0, 7) + '-01';
						}

						Object.values(downloads).forEach((item, index) => {
							weeksDownloads[chartDate] = ('undefined' !== typeof weeksDownloads[chartDate]) ? weeksDownloads[chartDate] + item.downloads : item.downloads;
						});

						dlmDownloads = weeksDownloads;
						break;
					case 'week':

						chartDate = day.date;
						Object.values(downloads).forEach((item, index) => {
							// If it does not exist we attach the downloads to the previous month
							if ('undefined' === typeof weekDownloads[chartDate]) {
								for (let $i = 1; $i < 8; $i++) {
									let $currentDayOfWeek = moment(day.date).date(moment(day.date).date() - $i).format("YYYY-MM-DD");
									if ('undefined' !== typeof weekDownloads[$currentDayOfWeek]) {
										weekDownloads[$currentDayOfWeek] = weekDownloads[$currentDayOfWeek] + item.downloads;
									}
								}
							} else {
								weekDownloads[chartDate] = weekDownloads[chartDate] + item.downloads;
							}
						});

						dlmDownloads = weekDownloads;
						break;
					case 'days':

						chartDate    = day.date;
						let prevDate = moment(day.date).date(moment(day.date).date() - 1).format("YYYY-MM-DD");

						Object.values(downloads).forEach((item, index) => {
							// If it does not exist we attach the downloads to the previous month
							if ('undefined' === typeof doubleDays[chartDate]) {
								if ('undefined' !== typeof doubleDays[prevDate]) {
									doubleDays[prevDate] = doubleDays[prevDate] + item.downloads;
								}
							} else {
								doubleDays[chartDate] = doubleDays[chartDate] + item.downloads;
							}
						});

						dlmDownloads = doubleDays;
						break;
					case 'day':

						Object.values(downloads).forEach((item, index) => {
							dayDownloads[day.date] = dayDownloads[day.date] + item.downloads;
						});

						dlmDownloads = dayDownloads;
						break;
				}
			} else {
				delete dlmReportsInstance.reportsData[index];
			}
		});

		// Get number of days, used in summary for daily average downloads
		const dayKeys    = Object.keys(dayDownloads);
		const daysLength = dayKeys.length;

		// Find the start of the downloads object
		let start = dayKeys.findIndex((element) => {
			return startDate === element;
		});

		// Find the end of the downloads object
		let end = dayKeys.findIndex((element) => {
			return endDate === element;
		});

		if (-1 === start && -1 === end) {

			dlmReportsInstance.stats = {
				chartStats  : Object.assign({}, dlmDownloads),
				summaryStats: false,
				daysLength
			};
			return;
		}

		if (-1 === start) {
			start = 0;
		}

		if (-1 === end) {
			end = daysLength;
		}

		dlmReportsInstance.stats = {
			chartStats  : Object.assign({}, dlmDownloads),
			summaryStats: dlmReportsInstance.reportsData,
			daysLength
		};
	}

	/**
	 * Let's create our chart.
	 *
	 * @param {*} data
	 * @param {*} chartId
	 * @param otherData Check if data passsed in the original chart data or some other, like the compare data
	 */
	dlmCreateChart(data, chartId, otherData = false) {

		if (data && chartId) {

			let chart = Chart.getChart('total_downloads_chart');

			dlmReportsInstance.chartDataObject = {
				dataSetLabel       : 'Downloads',
				dataSetColor       : '#27ae60',
				dataSetbg          : dlmReportsInstance.chartGradient,
				dataSetPointbg     : dlmReportsInstance.chartColors.darkCyan.default,
				dataSetBorder      : dlmReportsInstance.chartColors.darkCyan.default,
				dataSetElementColor: '#2ecc71',
				lineType           : 'original',
				xAxis              : 'x',
				chartData          : data
			}

			if ('undefined' !== typeof chart) {
				chart.destroy();
			}

			// Trigger this action here in order for us to tap into dlmReportsInstance.chartDataObject
			jQuery(document).trigger('dlm_reports_before_data_sets', [
				dlmReportsInstance.chartDataObject,
				data,
				otherData
			]);

			// Unset the dataSet that will be modified
			if (dlmReportsInstance.dataSets.length > 0) {
				dlmReportsInstance.dataSets = dlmReportsInstance.dataSets.filter((element) => {

					if (dlmReportsInstance.chartDataObject.lineType === element.origin) {
						return false;
					}

					return true;
				});
			}

			dlmReportsInstance.dataSets.push(
				{
					origin                   : dlmReportsInstance.chartDataObject.lineType,
					label                    : dlmReportsInstance.chartDataObject.dataSetLabel,
					color                    : dlmReportsInstance.chartDataObject.dataSetColor,
					data                     : dlmReportsInstance.chartDataObject.chartData,
					type                     : 'line',
					fill                     : true,
					backgroundColor          : dlmReportsInstance.chartDataObject.dataSetbg,
					pointBackgroundColor     : dlmReportsInstance.chartDataObject.dataSetPointbg,
					pointHoverBackgroundColor: '#fff',
					borderColor              : dlmReportsInstance.chartDataObject.dataSetBorder,
					pointBorderWidth         : 1,
					lineTension              : 0.3,
					borderWidth              : 1,
					pointRadius              : 3,
					elements                 : {
						line    : {
							borderColor: dlmReportsInstance.chartDataObject.dataSetElementColor,
							borderWidth: 1,
						}, point: {
							radius: 4, hoverRadius: 4, pointStyle: 'circle'
						}
					},
				}
			);

			// Get the original data, in case dataSets has otherData
			let currentData = Object.values(dlmReportsInstance.dataSets).filter((element) => {
				return 'original' === element.origin;
			});

			let trueData = Object.keys(currentData[0].data);
			// Sort the dataSets so that the Downloads data will always be first
			dlmReportsInstance.dataSets.sort(function (a, b) {
				if ('original' === a.origin) {
					return -1;
				}
				return 1;
			});

			dlmReportsInstance.chart = new Chart(chartId, {
				title    : "",
				data     : {
					datasets: dlmReportsInstance.dataSets
				},
				height   : 450,
				is_series: 1,
				options  : {
					aspectRatio: 5,
					animation  : false,
					interaction: {
						mode     : 'index',
						intersect: false,
					},
					stacked    : false,
					scales     : {
						x: {
							grid : {
								display: false,
							},
							ticks: {
								callback: (val) => {

									let date       = '';
									let dateString = '';

									dateString = trueData[val];

									const lastDate     = trueData[trueData.length - 1];
									const prevLastDate = moment(lastDate).month(moment(lastDate).month() - 1).format("YYYY-MM");

									if ('undefined' !== dlmReportsInstance.chartType && 'months' === dlmReportsInstance.chartType) {

										const month = moment(trueData[val]).month();

										if (11 > month) {
											if (dateString === prevLastDate) {
												date = moment(dateString).format("MMM, YYYY");
											} else {
												date = moment(dateString).format("MMM") + ' - ' + moment(dateString).month(month + 1).format("MMM") + moment(dateString).format(", YYYY");
											}

										} else {
											if (dateString === prevLastDate || dateString === lastDate) {
												date = moment(dateString).format("MMM, YYYY");
											} else {
												date = moment(dateString).format("MMM") + moment(dateString).format(" YYYY") + ' - ' + moment(dateString).month(month + 1).format("MMM") + moment(dateString).month(month + 1).format(", YYYY");
											}

										}
									} else if ('undefined' !== dlmReportsInstance.chartType && 'months' === dlmReportsInstance.chartType) {
										date = moment(dateString).format("MMMM, YYYY");
									} else {
										date = moment(dateString).format("D MMM");
									}

									return date;
								}
							},
						},
						y: {
							grid : {
								drawBorder: false,
							},
							min  : 0,
							max  : (0 !== dlmReportsInstance.getMaxDownload()) ? ( Math.ceil(dlmReportsInstance.getMaxDownload() / 10) === 1 ? dlmReportsInstance.getMaxDownload() + 1 : Math.ceil(dlmReportsInstance.getMaxDownload() / 10) * 10 ) : 100,
							ticks: {
								stepSize: (0 !== dlmReportsInstance.getMaxDownload()) ? Math.ceil(dlmReportsInstance.getMaxDownload() / 4) : 25,
								callback: (val) => {
									return dlmReportsInstance.shortNumber(val);
								}
							}
						}
					},
					normalized : true, parsing: {
						xAxisKey: 'x', yAxisKey: 'y'
					},
					plugins    : {
						tooltip: {
							enabled : false,
							external: dlmReportsInstance.externalTooltipHandler.bind(dlmReportsInstance, this),
						},
						legend : {
							display: true
						}
					},
				}
			});
		}
	}

	/**
	 * Our download summary based on the selected date range.
	 *
	 *
	 * @returns
	 */
	dlmDownloadsSummary() {

		let mostDownloaded = {};

		if (false === dlmReportsInstance.stats || false === dlmReportsInstance.stats.summaryStats || Object.keys(dlmReportsInstance.stats.summaryStats).length <= 0) {

			this.setTotalDownloads(0);
			this.setDailyAverage(0);
			this.setMostDownloaded('--');
			return;
		}
		dlmReportsInstance.totalDownloads = 0;
		// Lets prepare the items based on item id and not date so that we can get the most downloaded item
		dlmReportsInstance.stats.summaryStats.forEach((itemSet) => {

			itemSet = JSON.parse(itemSet.download_ids);

			Object.entries(itemSet).forEach(([key, item]) => {

				dlmReportsInstance.totalDownloads += item.downloads;
				mostDownloaded[key] = ('undefined' === typeof mostDownloaded[key]) ? {
					downloads: item.downloads, id: key, title: item.title
				} : {
					downloads: mostDownloaded[key]['downloads'] + item.downloads, id: key, title: item.title
				};
			});
		});

		dlmReportsInstance.mostDownloaded = dlmReportsInstance.orderItems(Object.values(mostDownloaded), 'desc', 'downloads');

		dlmReportsInstance.setTotalDownloads(dlmReportsInstance.totalDownloads);
		dlmReportsInstance.setDailyAverage((dlmReportsInstance.totalDownloads / parseInt(dlmReportsInstance.stats.daysLength)).toFixed(0));
		if ('undefined' !== typeof dlmReportsInstance.mostDownloaded[0]) {
			const mostDownloadedItem = dlmReportsInstance.getDownloadCPT(dlmReportsInstance.mostDownloaded[0].id);

			if ('undefined' !== typeof mostDownloadedItem) {
				dlmReportsInstance.setMostDownloaded(mostDownloadedItem.title.rendered);
			} else {
				dlmReportsInstance.setMostDownloaded(dlmReportsInstance.mostDownloaded[0].title);
			}
		}

	}

	/**
	 * Create our date picker
	 *
	 *
	 * @returns
	 */
	createDatepicker(target, opener, containerID) {

		const today = new Date();
		let dd      = today.getDate() - 1;
		let mm      = today.getMonth() + 1; //January is 0!
		let mmm     = mm - 1;
		const yyyy  = today.getFullYear();

		if (dd < 10) {
			dd = '0' + dd;
		}

		if (mm < 10) {
			mm = "0" + mm;
		}

		if (mmm < 10) {
			mmm = "0" + mmm;
		}
		const yesterday = yyyy + '-' + mm + '-' + dd;
		const lastMonth = yyyy + '-' + mmm + '-' + dd;

		var el        = jQuery('<div>').addClass('dlm_rdrs_overlay');
		var startDate = jQuery('<div>').attr('id', containerID.replace('#', ''));
		switch (opener.target) {
			case 'dlm-date-range-picker':
				dlmReportsInstance.startDateInput = jQuery('<input>').attr('type', 'hidden').attr('id', 'dlm_start_date').attr('value', lastMonth);
				dlmReportsInstance.endDateInput   = jQuery('<input>').attr('type', 'hidden').attr('id', 'dlm_end_date').attr('value', yesterday);
				el.append(startDate).append(dlmReportsInstance.startDateInput).append(dlmReportsInstance.endDateInput);
				break;
			default:
				jQuery(document).trigger('dlm_create_date_picker_' + opener.target, [
					dlmReportsInstance,
					el,
					startDate,
					lastMonth,
					yesterday
				]);
				break;
		}

		return el;
	}

	/**
	 * Display our date picker.
	 *
	 *
	 * @returns
	 */
	displayDatepicker(target, opener) {

		let containerID = '';

		if (!jQuery(target)) {
			return;
		}

		containerID = '#' + jQuery(target).attr('id').replace(/-/gi, '_');
		switch (opener.target) {
			case 'dlm-date-range-picker':
				if (dlmReportsInstance.datePicker.opened) {
					return;
				}
				dlmReportsInstance.datePicker.opened = true;
				break;
			default:
				jQuery(document).trigger('dlm_display_datepicker_' + opener.target, [
					dlmReportsInstance,
					opener,
					target
				]);
				break;
		}

		let element = dlmReportsInstance.createDatepicker(target, opener, containerID);
		target.append(element);
		const calendar_start_date = (Object.keys(dlmReportsInstance.dlmReportsStats).length > 0) ? new Date(dlmReportsInstance.dlmReportsStats[0].date) : new Date();
		const currDate            = new Date();
		let datepickerShortcuts   = [];

		// JS filter to add other shortcuts to the datepicker.
		jQuery(document).trigger('dlm_datepicker_shortcuts_' + opener.target, [dlmReportsInstance, opener, target, datepickerShortcuts]);

		var configObject = {
			separator      : ' to ',
			autoClose      : true,
			getValue       : function () {

			},
			setValue       : function (s, s1, s2) {
				element.find('input[type="hidden"]').first().val(s1);
				element.find('input[type="hidden"]').last().val(s2);

			},
			inline         : true,
			alwaysOpen     : true,
			container      : containerID, // End date should be current date
			endDate        : new Date(), // Start date should be the first info we get about downloads
			startDate      : calendar_start_date,
			showShortcuts  : true,
			shortcuts      : null,
			customShortcuts: datepickerShortcuts,
			startOfWeek    : dlmReportsInstance.weekstart, // sunday or monday
		};

		element.dateRangePicker(configObject).on('datepicker-change', (event, obj) => {

			if (obj.date1 && obj.date2) {

				const date_s = obj.date1.toLocaleDateString(undefined, {
					year: 'numeric', month: 'short', day: '2-digit'
				});

				const date_e = obj.date2.toLocaleDateString(undefined, {
					year: 'numeric', month: 'short', day: '2-digit'
				});

				element.parent().find('span.date-range-info').text(date_s + ' - ' + date_e);
			}

			switch (opener.target) {
				case 'dlm-date-range-picker':
					dlmReportsInstance.dates.downloads = {
						start_date: obj.date1,
						end_date  : obj.date2
					}
					// Recreate the stats
					dlmReportsInstance.createDataOnDate(dlmReportsInstance.dates.downloads.start_date, dlmReportsInstance.dates.downloads.end_date);

					dlmReportsInstance.dlmCreateChart(dlmReportsInstance.stats.chartStats, dlmReportsInstance.chartContainer, false);
					dlmReportsInstance.dlmDownloadsSummary();
					// This needs to be set after dlmReportsInstance.dlmDownloadsSummary() because it uses a variable set by it
					if (Object.values(dlmReportsInstance.dlmUsersStats.logs).length > 0) {
						dlmReportsInstance.logsDataByDate(dlmReportsInstance.dates.downloads.start_date, dlmReportsInstance.dates.downloads.end_date);
					}
					break;
				default:
					// JS action to trigger when custom datepicker is changed.
					jQuery(document).trigger('dlm_daterangepicker_init_' + opener.target, [dlmReportsInstance, obj.date1, obj.date2]);
					break;
			}
			dlmReportsInstance.setTopDownloads();
			element.data('dateRangePicker').close();
			jQuery(document).trigger('dlm_daterangepicker_on_close', [element, dlmReportsInstance]);
		});

		switch (opener.target) {
			case 'dlm-date-range-picker':
				element.data('dateRangePicker').setDateRange(dlmReportsInstance.dates.downloads.start_date, dlmReportsInstance.dates.downloads.end_date);
				break;
			default:
				jQuery(document).trigger('dlm_daterangepicker_after_init_' + opener.target, [element, dlmReportsInstance]);
				break;
		}
	}

	/**
	 * Hide the datepicker.
	 */
	hideDatepicker(target, opener) {

		switch (opener.target) {
			case 'dlm-date-range-picker' :
				dlmReportsInstance.datePicker.opened = false;
				break;
			default:
				jQuery(document).trigger('dlm_hide_datepicker_' + opener.target, [
					dlmReportsInstance,
					target,
					opener
				]);
				break;
		}

		target.find('.dlm_rdrs_overlay').remove();
	}

	/**
	 * Toggle the date picker on/off.
	 */
	toggleDatepicker(event) {

		event.stopPropagation();
		const target = jQuery(event.target).parents('.dlm-reports-header-date-selector');
		let opener   = {target: target.attr('id'), object: dlmReportsInstance.datePicker};
		dlmReportsInstance.closeDatePickers(target);
		switch (opener.target) {
			case 'dlm-date-range-picker':
				if (dlmReportsInstance.datePicker.opened) {
					dlmReportsInstance.hideDatepicker(target, opener);
				} else {
					dlmReportsInstance.displayDatepicker(target, opener);
				}
				break;
			default:
				jQuery(document).trigger('dlm_toggle_datepicker_' + opener.target, [
					dlmReportsInstance,
					target,
					opener
				]);
				break;
		}
	}

	/**
	 * Set total downloads based on the selected date range.
	 *
	 *
	 * @param {*} totalDownloads
	 */
	setTotalDownloads(totalDownloads) {
		jQuery('.dlm-reports-block-summary li#total span').html(totalDownloads.toLocaleString());
	}

	/**
	 * Set daily average based on the selected date range.
	 *
	 * @param {*} dailyAverage
	 */
	setDailyAverage(dailyAverage) {
		jQuery('.dlm-reports-block-summary li#average span').html(dailyAverage.toLocaleString());
	}

	/**
	 * Set most downloaded Download.
	 *
	 * @param {*} mostDownloaded
	 */
	setMostDownloaded(mostDownloaded) {
		jQuery('.dlm-reports-block-summary li#most_popular span').html(mostDownloaded); // this is a string
	}

	/**
	 * Set today's downloads.
	 */
	setTodayDownloads() {

		let todayDownloads = 0;

		if (0 >= Object.keys(dlmReportsInstance.dlmReportsStats).length) {

			jQuery('.dlm-reports-block-summary li#today span').html(todayDownloads.toLocaleString());
			return;
		}

		// We only need the last date from dlmReportsStats, as it will be the last entry from the DB in crhonological order.
		if (dlmReportsInstance.dlmReportsStats[dlmReportsInstance.dlmReportsStats.length - 1].date === dlmReportsInstance.createDateElement(new Date())) {

			todayDownloads = Object.values(JSON.parse(dlmReportsInstance.dlmReportsStats[dlmReportsInstance.dlmReportsStats.length - 1].download_ids)).reduce((prevValue, element) => {

				return prevValue + element.downloads;
			}, 0);

		}

		jQuery('.dlm-reports-block-summary li#today span').html(todayDownloads);
	}

	/**
	 * Set the top downloads with fetched templates.
	 *
	 * @param {*} offset
	 * @param {*} reset
	 * @returns
	 */
	setTopDownloads(offset = 0, reset = false) {
		// the table
		const wrapperParent = jQuery('#total_downloads_table_wrapper2'),
			  wrapper       = jQuery('#total_downloads_table_wrapper2 .total_downloads_table__list');

		wrapper.empty();
		wrapper.parent().addClass('empty');

		if (!dlmReportsInstance.mostDownloaded || true === reset) {
			return;
		}

		const dataResponse = JSON.parse(JSON.stringify(dlmReportsInstance.mostDownloaded)).slice(dlmReportsInstance.perPage * parseInt(offset), dlmReportsInstance.perPage * (parseInt(offset + 1)));

		for (let i = 0; i < dataResponse.length; i++) {

			const $download    = dlmReportsInstance.getDownloadByID(dataResponse[i].id);
			let $downloadCPT = dlmReportsInstance.getDownloadCPT(dataResponse[i].id);
			let title = '--';

			if ( 'undefined' === typeof $downloadCPT) {
				title = dataResponse[i].title;
			} else {
				title = $downloadCPT.title.rendered
			}

			// No point on showing the download if it doesn't exist
			if ('undefined' === typeof $download) {
				return
			}

			let itemObject = {
				id             : dataResponse[i].id,
				title          : dlmReportsInstance.htmlEntities( title ),
				edit_link      : dlmAdminUrl + 'post.php?post=' + dataResponse[i].id + '&action=edit',
				total_downloads: $download.total.toLocaleString()
			};

			// Trigger used to model the itemObject passed to the template
			jQuery(document).trigger('dlm_reports_top_downloads_item_before_render', [itemObject, dlmReportsInstance, dataResponse[i], $download]);

			let item = new dlmBackBone['modelTopDownloads'](itemObject);
		}

		wrapper.parent().removeClass('empty');
		// Set top downloads number of pages
		wrapperParent.find('.dlm-reports-total-pages').html(Math.ceil(dlmReportsInstance.mostDownloaded.length / dlmReportsInstance.perPage));

		if (parseInt(dlmReportsInstance.perPage) !== parseInt(dataResponse.length)) {
			wrapperParent.find('.downloads-block-navigation button[data-action="load-more"]').attr('disabled', 'disabled');
		} else {
			wrapperParent.find('.downloads-block-navigation button[data-action="load-more"]').removeAttr('disabled');
		}

		if (dlmReportsInstance.mostDownloaded.length > dlmReportsInstance.perPage) {
			wrapperParent.find('.downloads-block-navigation button').removeClass('hidden');
		} else {
			wrapperParent.find('.downloads-block-navigation button').addClass('hidden');
		}
		dlmReportsInstance.stopSpinner(jQuery('#total_downloads_table_wrapper2'));
		jQuery(document).trigger( 'dlm_reports_top_downloads_table_rendered', [wrapperParent, wrapper, dlmReportsInstance, dataResponse] );
	}

	/**
	 * Let's create the slider navigation. Will be based on offset
	 */
	handleTopDownloads() {

		jQuery('html body').on('click', '#total_downloads_table_wrapper2 .downloads-block-navigation button', function () {

			let main_parent                               = jQuery(this).parents('#total_downloads_table_wrapper2'),
				offsetHolder                              = main_parent,
				offset                                    = main_parent.attr('data-page'),
				link                                      = jQuery(this),
				nextPage = parseInt(offset) + 1, prevPage = (0 !== offset) ? parseInt(offset) - 1 : 0,
				prevButton                                = main_parent.find('.downloads-block-navigation').find('button').first(),
				nextButton                                = main_parent.find('.downloads-block-navigation').find('button').last();

			link.attr('disabled', 'disabled');
			const handleObj = {
				data    : dlmReportsInstance.mostDownloaded,
				main_parent,
				offsetHolder,
				offset,
				link,
				nextPage,
				prevPage,
				prevButton,
				nextButton,
				doAction: dlmReportsInstance.setTopDownloads
			}

			dlmReportsInstance.handleSliderNavigation(handleObj)
		});

		jQuery('#total_downloads_table_wrapper2').find('input.dlm-reports-current-page').on('change', function () {
			dlmReportsInstance.paginationChange(jQuery(this), dlmReportsInstance.mostDownloaded, jQuery('#total_downloads_table_wrapper2'), jQuery(this).parents('#total_downloads_table_wrapper2'), dlmReportsInstance.setTopDownloads);
		});
	}

	/**
	 * Slider navigation
	 *
	 * @param handleObj
	 */
	handleSliderNavigation(handleObj) {
		const {
				  data,
				  main_parent,
				  offsetHolder,
				  offset,
				  link,
				  nextPage,
				  prevPage,
				  prevButton,
				  nextButton,
				  doAction
			  } = {...handleObj};

		let page = 1;

		// Check if we click the next/load more button
		if ('load-more' === link.data('action')) {

			offsetHolder.attr('data-page', nextPage);
			doAction(nextPage);
			// We remove the disable attribute only when there are pages to be shown
			if (Math.ceil(data.length / dlmReportsInstance.perPage) > nextPage + 1) {
				nextButton.removeAttr('disabled');
			}
			prevButton.removeAttr('disabled');
			page = parseInt(nextPage) + 1;
		} else {

			if (0 !== parseInt(offset)) {

				//table.toggle({ effect: "scale", direction: "both" });
				offsetHolder.attr('data-page', prevPage);
				doAction(prevPage);
				// Remove on all buttons, as it will be at least page 2
				if (1 !== parseInt(offset)) {
					prevButton.removeAttr('disabled');
				}
				nextButton.removeAttr('disabled');
				page = parseInt(prevPage) + 1;
			}
		}

		main_parent.find('.dlm-reports-current-page').val(page);
	}

	/**
	 * Tab navigation
	 */
	tabNagivation() {
		jQuery(document).on('click', '.dlm-reports .dlm-insights-tab-navigation > li', function () {
			const listClicked     = jQuery(this),
				  navLists        = jQuery('.dlm-reports .dlm-insights-tab-navigation > li').not(listClicked),
				  contentTarget   = jQuery('div.dlm-insights-tab-navigation__content[data-id="' + listClicked.attr('id') + '"]'),
				  contentWrappers = jQuery('div.dlm-insights-tab-navigation__content').not(contentTarget);

			if (!listClicked.hasClass('active')) {
				listClicked.addClass('active');
				navLists.removeClass('active');
				contentTarget.addClass('active');
				contentWrappers.removeClass('active');
			}
			if( 'general_info' != listClicked.attr('id') ){
				jQuery('.dlm-reports').removeClass('general_info' );
			}else{
				jQuery('.dlm-reports').addClass('general_info' );
			}
			jQuery(document).trigger('dlm_reports_tab_change_' + listClicked.attr('id'), [listClicked, navLists, contentTarget, contentWrappers]);
		});
	}

	/**
	 * The external tooltip of the Chart
	 *
	 * @param chart
	 * @returns {*}
	 */
	getOrCreateTooltip(chart) {

		let tooltipEl   = chart.canvas.parentNode.querySelector('div.dlm-canvas-tooltip');
		let tooltipLine = chart.canvas.parentNode.querySelector('div.dlm-reports-tooltip__line');

		if (!tooltipEl) {
			tooltipLine           = document.createElement('div');
			tooltipLine.className = "dlm-reports-tooltip__line";
		}

		if (!tooltipEl) {
			tooltipEl           = document.createElement('div');
			tooltipEl.className = "dlm-canvas-tooltip";

			const tooltipWrapper     = document.createElement('div');
			tooltipWrapper.className = "dlm-reports-tooltip";
			tooltipEl.appendChild(tooltipWrapper);

			chart.canvas.parentNode.appendChild(tooltipEl);
			chart.canvas.parentNode.appendChild(tooltipLine);
		}

		return {tooltipEl, tooltipLine};
	}

	/**
	 * The external tooltip of the Chart handler
	 *
	 * @param plugin
	 * @param context
	 */
	externalTooltipHandler(plugin, context) {

		// Tooltip Element
		const {
				  chart, tooltip
			  } = context;

		const {tooltipEl, tooltipLine} = {...plugin.getOrCreateTooltip(chart)};
		const tooltipWidth             = jQuery(tooltipEl).parent().width();

		// Hide if no tooltip
		if (tooltip.opacity === 0) {
			tooltipEl.style.opacity   = 0;
			tooltipLine.style.opacity = 0;
			return;
		}

		// Set Text
		if (tooltip.body) {
			const titleLines = tooltip.title || [];

			const tooltipContent     = document.createElement('div');
			tooltipContent.className = "dlm-reports-tooltip__header";

			titleLines.forEach(title => {
				const tooltipRow     = document.createElement('div');
				tooltipRow.className = "dlm-reports-tooltip__row";

				// Info
				const downloadsInfo     = document.createElement('p');
				downloadsInfo.className = "dlm-reports-tooltip__info";
				downloadsInfo.appendChild(document.createTextNode('Downloads'));
				tooltipRow.appendChild(downloadsInfo);

				jQuery(document).trigger('dlm_chart_tooltip_before', [
					dlmReportsInstance,
					tooltip,
					tooltipRow,
					plugin
				]);

				// Date
				const downloadDate     = document.createElement('p');
				downloadDate.className = "dlm-reports-tooltip__date";

				let date = dlmReportsInstance.setChartTooltipDate(tooltip.dataPoints[0].label, plugin, plugin.stats.chartStats);

				downloadDate.appendChild(document.createTextNode(date));
				tooltipRow.appendChild(downloadDate);

				// Downloads number
				const downloads     = document.createElement('p');
				downloads.className = "dlm-reports-tooltip__downloads";

				// Pointer for the downloads chart
				const downloadsPointer                 = document.createElement('span');
				downloadsPointer.className             = "dlm-reports-tooltip__downloads_pointer";
				downloadsPointer.style.backgroundColor = dlmReportsInstance.chartColors.darkCyan.default;
				downloads.appendChild(downloadsPointer);

				downloads.appendChild(document.createTextNode(dlmReportsInstance.shortNumber(tooltip.dataPoints[0].formattedValue)));
				tooltipRow.appendChild(downloads);

				jQuery(document).trigger('dlm_chart_tooltip_after', [
					dlmReportsInstance,
					tooltip,
					tooltipRow,
					plugin
				]);

				// Create the whole content and append it
				tooltipContent.appendChild(tooltipRow);
			});

			const tooltipWRapper = tooltipEl.querySelector('div.dlm-reports-tooltip');

			// Remove old children
			while (tooltipWRapper.firstChild) {
				tooltipWRapper.firstChild.remove();
			}

			// Add new children
			tooltipWRapper.appendChild(tooltipContent);
		}

		const {
				  offsetLeft: positionX, offsetTop: positionY
			  } = chart.canvas;

		// Display, position, and set styles for font
		tooltipEl.style.opacity   = 1;
		tooltipLine.style.opacity = 1;
		let margin                = {
			isMargin: false, left: false
		};

		if (tooltip.caretX - tooltip.width < 0) {
			margin.isMargin = true;
			margin.left     = true;
		}

		if (positionX + tooltip.caretX + tooltip.width > tooltipWidth) {
			margin.isMargin = true;
			margin.left     = false;
		}

		if (!margin.isMargin) {
			tooltipEl.style.left = positionX + tooltip.caretX + 'px';

		} else {
			if (!margin.left) {
				tooltipEl.style.left = tooltipWidth - tooltip.width + 'px';
			} else {
				tooltipEl.style.left = positionX + tooltip.width + 'px';
			}
		}
		tooltipLine.style.left = positionX + tooltip.caretX + 'px';

		tooltipEl.style.top = (positionY + tooltip.caretY - tooltipEl.offsetHeight - 10) + 'px';
	}

	/**
	 * Create user related data
	 */
	createUserRelatedData() {

		dlmReportsInstance.userRelatedData = [];

		Object.values(dlmReportsInstance.userDownloads).forEach((download, index) => {
			if ('0' !== download.user_id) {
				const insert_array = [
					download.user_id,
					download.download_id,
					download.download_date,
					download.download_status
				];
				const user_key     = 'user_' + download.user_id;
				if ('undefined' !== typeof dlmReportsInstance.userRelatedData[user_key]) {
					dlmReportsInstance.userRelatedData[user_key].push(insert_array);
				} else {
					dlmReportsInstance.userRelatedData[user_key] = [insert_array];
				}
			}
		});
	}

	/**
	 * Create logs data by date
	 *
	 * @param startDateInput
	 * @param endDateInput
	 */
	logsDataByDate(startDateInput, endDateInput) {
		let {startDate, endDate}         = {...dlmReportsInstance.getSetDates(startDateInput, endDateInput)};
		dlmReportsInstance.userDownloads = JSON.parse(JSON.stringify(dlmReportsInstance.dlmUsersStats.logs));
		let startTimestamp               = new Date(startDate)
		startTimestamp.setDate(startTimestamp.getDate() - 1);
		startTimestamp = startTimestamp.getTime();

		let endTimestamp = new Date(endDate);
		endTimestamp.setDate(endTimestamp.getDate() + 1);
		endTimestamp = endTimestamp.getTime();
		dlmReportsInstance.userDownloads = dlmReportsInstance.userDownloads.filter((element, index) => {
			let currentElement = dlmReportsInstance.createDateElement(new Date(element.download_date));
			currentElement     = new Date(currentElement).getTime();
			return (currentElement > startTimestamp && currentElement < endTimestamp);
		});

		dlmReportsInstance.createUserRelatedData();
		dlmReportsInstance.filterDownloads()
		dlmReportsInstance.setMostActiveUser();
		dlmReportsInstance.setLoggedOutDownloads();
		dlmReportsInstance.setLoggedInDownloads();
		jQuery(document).trigger('dlm_set_logs_data_by_date', [dlmReportsInstance]);
	}

	/**
	 * Set the most active user
	 */
	setMostActiveUser() {
		const user = dlmReportsInstance.getUserByID(dlmReportsInstance.getMostActiveID()[0]);
		jQuery('.dlm-reports-block-summary li#most_active_user span').html(dlmReportsInstance.userToolTipMarkup(user));
	}

	/**
	 * Get the most active user
	 */
	getMostActiveID() {
		if (Object.values(dlmReportsInstance.userRelatedData).length) {
			return Object.values(dlmReportsInstance.userRelatedData).reduce((previousValue, currentValue, currentIndex) => {

				if (parseInt(previousValue.length) > parseInt(currentValue.length) && previousValue.length > 0 && null !== dlmReportsInstance.getUserByID(previousValue[0][0])) {
					return previousValue;
				}
				if (null !== dlmReportsInstance.getUserByID(currentValue[0][0])) {
					return currentValue;
				}

				return [];

			}, []);
		}
		return 0;
	}

	/**
	 * Get user by ID
	 * @param user_id
	 */
	getUserByID(user_id) {

		if (!user_id) {
			return null;
		}
		if ('0' === user_id) {
			return {
				role:'Guest',
				display_name:'Guest',
			};
		}

		let $user = Object.values(dlmReportsInstance.dlmUsersStats.users).filter(user => {
			return parseInt(user_id) === parseInt(user.id);
		});

		if (Array.isArray($user)) {
			if (0 === $user.length) {
				return null;
			}
			return $user[0];
		}

		return $user;
	}

	/**
	 * Get number of logged in downloads
	 * @returns {number}
	 */
	getLoggedInDownloads() {

		if (Object.values(dlmReportsInstance.userRelatedData).length) {
			if (Object.values(dlmReportsInstance.userRelatedData).length > 1) {
				return Object.values(dlmReportsInstance.userRelatedData).reduce((previousValue, currentValue) => {
					return parseInt(previousValue) + parseInt(currentValue.length);
				}, 0);
			} else {
				return Object.values(dlmReportsInstance.userRelatedData)[0].length;
			}

		}
		return 0;
	}

	/**
	 * Set total logged in stats
	 */
	setLoggedInDownloads() {
		const stat = dlmReportsInstance.getLoggedInDownloads();

		jQuery('.dlm-reports-block-summary li#logged_in span,#total_downloads_summary_wrapper .dlm-reports-logged-in').html(stat.toLocaleString());
	}

	/**
	 * Get logged out number
	 *
	 * @returns {number}
	 */
	getLoggedOutDownloads() {
		const all      = dlmReportsInstance.userDownloads.length;
		const loggedIn = dlmReportsInstance.getLoggedInDownloads();

		return all - loggedIn;
	}

	/**
	 * Set total logged out stats
	 */
	setLoggedOutDownloads() {
		const stat = dlmReportsInstance.getLoggedOutDownloads();

		jQuery('.dlm-reports-block-summary li#logged_out span,#total_downloads_summary_wrapper .dlm-reports-logged-out').html(stat.toLocaleString());
	}

	/**
	 * The tooltip markup for user's extra info
	 * @param user
	 */
	userToolTipMarkup(user) {

		let html = '<div class="dlm-user-reports">';
		html += '<div class="wpchill-tooltip"><i>[?]</i>';
		html += '<div class="wpchill-tooltip-content">';

		html += '<span>User ID: ' + ((null !== user) ? user.id : '--') + '</span>';

		if ('object' !== typeof user && user.url.length) {
			html += '<span>User URL: ' + ((null !== user) ? user.url : '--') + '</span>';
		}

		html += '<span>User registration date: ' + ((null !== user) ? user.registered : '--') + '</span>';

		if (null !== user && 'undefined' !== typeof user.role && user.role.length) {
			html += '<span>User role: ' + user.role + '</span>';
		}

		html += '</div></div>';
		html += ((null !== user) ? user.display_name : '--');
		html += '</div>';

		return html;
	}

	/**
	 * Generate the user downloads table
	 */
	setUserDownloads(offset = 0, reset = false) {
		// the table
		const wrapperParent = jQuery('#users_download_log'),
			  wrapper       = jQuery('#users_download_log .user-logs__list');
		wrapper.empty();

		if (true === reset) {
			return;
		}

		let dataResponse = [];

		if (null !== dlmReportsInstance.tempDownloads) {
			dataResponse = JSON.parse(JSON.stringify(dlmReportsInstance.tempDownloads)).slice(dlmReportsInstance.perPage * parseInt(offset), dlmReportsInstance.perPage * (parseInt(offset + 1)));
		} else {
			dataResponse = JSON.parse(JSON.stringify(dlmReportsInstance.userDownloads)).slice(dlmReportsInstance.perPage * parseInt(offset), dlmReportsInstance.perPage * (parseInt(offset + 1)));
		}

		for (let i = 0; i < dataResponse.length; i++) {
			const user = dlmReportsInstance.getUserByID(dataResponse[i].user_id.toString());
			let download = dlmReportsInstance.getDownloadCPT(parseInt(dataResponse[i].download_id));
			let title = '--';

			if ('undefined' === typeof download) {
				download = dlmReportsInstance.altGetDownloadCPT(dataResponse[i].download_id.toString());
				if ('undefined' !== typeof download) {
					title = download.title;
				}
			} else {
				title = download.title.rendered;
			}

			let itemObject = {
				key               : i,
				user              : ('undefined' !== typeof user && null !== user) ? user['display_name'] : '--',
				ip                : dataResponse[i].user_ip,
				role              : (null !== user && null !== user.role ? user.role : '--'),
				download          : ('undefined' !== typeof download && null !== download) ? dlmReportsInstance.htmlEntities(title) : '--',
				valid_user        : ('0' !== dataResponse[i].user_id),
				edit_link         : ( '0' !== dataResponse[i].user_id) ? 'user-edit.php?user_id=' + dataResponse[i].user_id : '#',
				edit_download_link: ('undefined' !== typeof download && null !== download) ? dlmAdminUrl + 'post.php?post=' + download.id + '&action=edit' : '#',
				status            : ( 'redirect' === dataResponse[i].download_status ) ? 'redirected' : dataResponse[i].download_status,
				download_date     : dataResponse[i].display_date,
			}

			jQuery(document).trigger('dlm_reports_user_logs_item_before_render', [itemObject, dlmReportsInstance, dataResponse[i], user, download]);

			let item = new dlmBackBone['modelUserLogs'](itemObject);
		}

		dlmReportsInstance.stopSpinner(jQuery('#users_download_log'));
		// Set the total number of downloads pages
		wrapperParent.find('.dlm-reports-total-pages').html(Math.ceil(dlmReportsInstance.tempDownloads.length / dlmReportsInstance.perPage));

		if (parseInt(dlmReportsInstance.perPage) !== parseInt(dataResponse.length)) {
			wrapperParent.find('.user-downloads-block-navigation button[data-action="load-more"]').attr('disabled', 'disabled');
		} else {
			wrapperParent.find('.user-downloads-block-navigation button[data-action="load-more"]').removeAttr('disabled');
		}

		if (dlmReportsInstance.userDownloads.length > dlmReportsInstance.perPage) {
			wrapperParent.find('.user-downloads-block-navigation button').removeClass('hidden');
		} else {
			wrapperParent.find('.user-downloads-block-navigation button').addClass('hidden');
		}
		jQuery(document).trigger( 'dlm_reports_user_logs_table_rendered', [dlmReportsInstance, wrapperParent, wrapper, dataResponse] );
	}

	/**
	 * Filter our downloads
	 */
	filterDownloads() {

		dlmReportsInstance.tempDownloads = JSON.parse(JSON.stringify(dlmReportsInstance.userDownloads));
		if (!dlmReportsInstance.currentFilters.length) {
			dlmReportsInstance.setUserDownloads();
			return;
		}

		dlmReportsInstance.currentFilters.forEach((filter) => {
			switch (filter.type) {
				case 'download_status':
					dlmReportsInstance.tempDownloads = dlmReportsInstance.tempDownloads.filter((element) => {
						if ('redirected' === filter.on) {
							return filter.on === element[filter.type] || 'redirect' === element[filter.type];
						}
						return filter.on === element[filter.type];
					});
					break;
				case 'user_id':
					dlmReportsInstance.tempDownloads = dlmReportsInstance.tempDownloads.filter((element) => {
						return filter.on === element[filter.type];
					});
					break;
				default:
					jQuery(document).trigger('dlm_reports_filter_downloads_' + filter.type, [dlmReportsInstance, filter, dlmReportsInstance.tempDownloads]);
					break;
			}
		});
		dlmReportsInstance.setUserDownloads();
	}

	/**
	 * Let's create the slider navigation. Will be based on offset
	 */
	handleUserDownloads() {

		jQuery('#users_download_log').on('click', '.user-downloads-block-navigation button', function (e) {
			e.stopPropagation();

			let main_parent  = jQuery(this).parents('#users_downloads_table_wrapper'),
				offsetHolder = main_parent.find('#users_download_log'),
				offset       = offsetHolder.attr('data-page'),
				link         = jQuery(this),
				nextPage     = parseInt(offset) + 1,
				prevPage     = (0 !== offset) ? parseInt(offset) - 1 : 0,
				prevButton   = main_parent.find('.downloads-block-navigation button').first(),
				nextButton   = main_parent.find('.downloads-block-navigation button').last();

			link.attr('disabled', 'disabled');

			const handleObj = {
				data    : dlmReportsInstance.tempDownloads,
				main_parent,
				offsetHolder,
				offset,
				link,
				nextPage,
				prevPage,
				prevButton,
				nextButton,
				doAction: dlmReportsInstance.setUserDownloads

			}
			dlmReportsInstance.handleSliderNavigation(handleObj);
		});

		jQuery('#users_downloads_table_wrapper').find('input.dlm-reports-current-page').on('change', function () {
			dlmReportsInstance.paginationChange(jQuery(this), dlmReportsInstance.tempDownloads, jQuery('#users_downloads_table_wrapper'), jQuery('#users_downloads_table_wrapper').find('#users_download_log'), dlmReportsInstance.setUserDownloads);
		});
	}

	/**
	 * Page settings area show/hide
	 */
	togglePageSettings() {
		jQuery('#dlm-toggle-settings').on('click', function (e) {
			e.stopPropagation();
			jQuery(this).find('.dlm-toggle-settings__settings').toggleClass('display');
		});
		jQuery('.dlm-toggle-settings__settings').on('click', function (e) {
			e.stopPropagation();
		});
		jQuery('html,body').on('click', function () {
			jQuery(this).find('.dlm-toggle-settings__settings').removeClass('display');
		});

		jQuery(document).on('change', '.wpchill-toggle__input', function (e) {
			const $this = jQuery(this), name = $this.attr('name'), data = {
				action     : 'dlm_update_report_setting',
				name       : name,
				checked    : $this.is(':checked'),
				_ajax_nonce: dlmReportsNonce
			};

			jQuery.post(ajaxurl, data, function (response) {
				switch (name) {
					default:
						jQuery(document).trigger('dlm_settings_ajax_response', [
							dlmReportsInstance,
							$this,
							response
						]);
						break;
				}
			});
		});
	}

	/**
	 * Get maximum downloaded times in one tick from chart
	 * @returns {number}
	 */
	getMaxDownload() {
		let max = 0;
		dlmReportsInstance.dataSets.forEach((element) => {
			let i = Object.values(element.data).reduce((prev, curr) => {
				if (prev > curr) {
					return prev;
				}
				return curr;
			}, 0);
			if (max < i) {
				max = i;
			}
		});

		return parseInt(max);
	}

	/**
	 * Set display date for the Chart Tooltip
	 *
	 * @param dateInput
	 * @param plugin
	 * @param dataSet
	 * @returns {*}
	 */
	setChartTooltipDate(dateInput, plugin, dataSet) {
		let date = '';
		if ('undefined' !== plugin.chartType && 'months' === plugin.chartType) {

			const year         = moment(dateInput).year();
			const month        = moment(dateInput).month();
			const lastDate     = Object.keys(dataSet)[Object.keys(dataSet).length - 1];
			const prevLastDate = moment(lastDate).month(moment(lastDate).month() - 1).format("YYYY-MM");
			const dateString   = moment(dateInput).format("YYYY-MM");

			if (11 > month) {

				if (dateString === prevLastDate) {

					date = moment(dateString).format("MMMM, YYYY");
				} else {

					date = moment(dateInput).format("MMM") + ' - ' + moment(dateInput).month(month + 1).format("MMM") + moment(dateInput).format(", YYYY");
				}

			} else {

				if (dateString === prevLastDate || dateString === lastDate) {

					date = moment(dateString).format("MMMM, YYYY");
				} else {

					date = moment(dateInput).format("MMM") + moment(dateInput).format(" YYYY") + ' - ' + moment(dateInput).month(month + 1).format("MMM") + moment(dateInput).month(month + 1).format(", YYYY");
				}

			}

		} else if ('undefined' !== plugin.chartType && 'days' === plugin.chartType) {

			const year         = moment(dateInput).year();
			const day          = moment(dateInput).day();
			const dayMonth     = moment(dateInput).format("MMMM");
			const nextDayMonth = moment(dateInput).day(day + 1).format("MMMM");
			const lastDate     = dlmReportsInstance.dates.downloads.end_date;
			const prevLastDate = moment(lastDate).day(moment(lastDate).day() - 1).format("MMMM Do");

			if (moment(dateInput).format("MMMM Do") === moment(lastDate).format("MMMM Do") || moment(dateInput).format("MMMM Do") === prevLastDate) {
				date = moment(dateInput).format("MMMM Do, YYYY");
			} else {
				if (dayMonth === nextDayMonth) {
					date = moment(dateInput).format("MMMM Do") + ' - ' + moment(dateInput).day(day + 1).format("Do") + moment(dateInput).format(", YYYY");
				} else {
					date = moment(dateInput).format("MMM Do") + ' - ' + moment(dateInput).day(day + 1).format("MMM Do") + moment(dateInput).format(", YYYY");
				}
			}

		} else {
			date = moment(dateInput).format("MMMM Do, YY");
		}

		return date;
	}

	/**
	 * Close all date pickers except the targeted one
	 *
	 * @param target
	 */
	closeDatePickers(target) {
		jQuery('.dlm-reports-header-date-selector').not(target).each(function () {
			const opener = {target: jQuery(this).attr('id')};
			dlmReportsInstance.hideDatepicker(jQuery(this), opener);
		})
	}

	/**
	 * Return short number in the form of 1K, 22K ....
	 *
	 * @param $number
	 * @returns {string}
	 */
	shortNumber($number) {

		if ('string' === typeof $number) {
			$number = $number.replace(/,/gi, '');
		} else {
			$number = parseInt($number).toString();
		}

		if ($number.length >= 4) {
			$number = parseInt($number.substring(0, $number.length - 3)).toLocaleString() + 'k';
		}

		return $number;
	}

	/**
	 * Get download object based on ID
	 * @param $id
	 * @returns {{total: number}}
	 */
	getDownloadByID($id) {

		let download = {
			total     : 0,
		},
		currentItem = {};
		dlmReportsInstance.tempDownloads.forEach(
			function (item) {
				if ($id === item.download_id) {
					currentItem = item;
					download.total = download.total + 1;

					jQuery(document).trigger('dlm_download_by_id', [dlmReportsInstance, download, currentItem]);
				}
			}
		);

		return download;
	}

	/**
	 * Get download object based on ID
	 * @param $id
	 * @returns {{id: number, title: string}}
	 */
	getDownloadCPT($id) {
		let download = null;

		if (Array.isArray(dlmReportsInstance.downloads)) {
			download = dlmReportsInstance.downloads.filter((item) => {
				return parseInt( item.id ) === parseInt( $id );
			}, 0)[0];
		}

		jQuery(document).trigger('dlm_download_cpt', [dlmReportsInstance, download]);

		return download;
	}

	/**
	 * Set loading spinner
	 * @param target
	 */
	setSpinner(target) {
		let spinnerHTML = '<div class="dlm-reports-spinner"><span></span></div>';
		target.append(spinnerHTML);
	}

	/**
	 * Remove loading spinner
	 * @param target
	 */
	stopSpinner(target) {
		target.find('.dlm-reports-spinner').remove();
	}
	eventsFunctions() {
		jQuery('body').on('click', '.total_downloads_table_filters_total_downloads > a', function (e) {
			e.preventDefault();
			if ('count' === dlmReportsInstance.topDownloadsOrder) {
				jQuery(this).parent().find('span.dashicons').toggleClass('dashicons-arrow-down dashicons-arrow-up');
			} else {
				jQuery(this).parent().find('span.dashicons').removeClass().addClass('dashicons dashicons-arrow-down');
			}
			dlmReportsInstance.orderOverviewItemsByTotal();
		});

		jQuery('body').on('click', '.total_downloads_table_filters_title > a', function (e) {
			e.preventDefault();
			if ('title' === dlmReportsInstance.topDownloadsOrder) {
				jQuery(this).parent().find('span.dashicons').toggleClass('dashicons-arrow-down dashicons-arrow-up');
			} else {
				jQuery(this).parent().find('span.dashicons').removeClass().addClass('dashicons dashicons-arrow-up');
			}
			dlmReportsInstance.orderOverviewItemsByTitle();
		});

		jQuery('body').on('click', '.total_downloads_table_filters_download_date > a', function (e) {
			e.preventDefault();
			jQuery(this).parent().find('span.dashicons').toggleClass('dashicons-arrow-down dashicons-arrow-up');
			dlmReportsInstance.orderUserReportsItemsByDate();
		});

		jQuery('body').on('change', 'select.dlm-reports-per-page', function (e) {
			dlmReportsInstance.perPage = jQuery(this).val();
			dlmReportsInstance.setTopDownloads();
			dlmReportsInstance.setUserDownloads();
			jQuery.post(
				ajaxurl,
				{
					action     : 'dlm_update_report_setting',
					name       : 'dlm-reports-per-page',
					value      : dlmReportsInstance.perPage,
					_ajax_nonce: dlmReportsNonce
				}, function (response) {
				}
			);
		});
	}
	/**
	 * Set order of items
	 *
	 * @since 4.6.1
	 */
	orderItems(items, order, key, setUploads = false) {

		items.sort((a, b) => {
			switch (order) {
				case 'asc':
					return a[key] - b[key];
				case 'desc':
					return b[key] - a[key];
				default:
					return b[key] - a[key];
			}
		});

		return items;
	}
	/**
	 * Revert the current set order of the Overview tab Top Downloads table
	 *
	 * @since 4.6.1
	 */
	orderOverviewItemsByTotal(){
		if ('count' !== dlmReportsInstance.topDownloadsOrder) {
			dlmReportsInstance.topDownloadsOrder = 'count';
			dlmReportsInstance.mostDownloaded = dlmReportsInstance.orderItems(dlmReportsInstance.mostDownloaded, 'desc', 'downloads');
		} else {
			dlmReportsInstance.mostDownloaded = dlmReportsInstance.mostDownloaded.reverse();
		}

		dlmReportsInstance.setTopDownloads();
	}
	/**
	 * Revert the current set order of the User Reports tab Logs tabel
	 *
	 * @since 4.6.1
	 */
	orderUserReportsItemsByDate(){
		dlmReportsInstance.tempDownloads = dlmReportsInstance.tempDownloads.reverse();
		dlmReportsInstance.setUserDownloads();
	}
	/**
	 * Revert the current set order of the Overview tab Top Downloads table
	 *
	 * @since 4.6.1
	 */
	orderOverviewItemsByTitle(){
		if ('title' !== dlmReportsInstance.topDownloadsOrder) {
			dlmReportsInstance.topDownloadsOrder = 'title';
			dlmReportsInstance.mostDownloaded.sort(function (a, b) {
				const downloadA = dlmReportsInstance.getDownloadCPT(a.id),
					  downloadB = dlmReportsInstance.getDownloadCPT(b.id),
					  aTitle    = ('undefined' !== typeof downloadA) ? dlmReportsInstance.getDownloadCPT(a.id).title.rendered.toLowerCase() : dlmReportsInstance.altGetDownloadCPT(a.id)['title'],
					  bTitle    = ('undefined' !== typeof downloadB) ? dlmReportsInstance.getDownloadCPT(b.id).title.rendered.toLowerCase() : dlmReportsInstance.altGetDownloadCPT(b.id)['title'];

				if (aTitle < bTitle) {
					return -1;
				}
				if (aTitle > bTitle) {
					return 1;
				}
				return 0;
			});
		} else {
			dlmReportsInstance.mostDownloaded = dlmReportsInstance.mostDownloaded.reverse();
		}

		dlmReportsInstance.setTopDownloads();
	}
	/**
	 * Pagination changing using the input type number
	 *
	 * @param input
	 * @param data
	 * @param main_parent
	 * @param offsetHolder
	 * @param action
	 */
	paginationChange( input, data, main_parent, offsetHolder, action ){

		let offset  = parseInt(input.val());

		if (0 === offset) {
			offset = 1;
		}

		if (data.length < (offset * dlmReportsInstance.perPage)) {
			offset = Math.ceil(data.length / dlmReportsInstance.perPage);
		}

		let link         = jQuery(this).next('button[data-action="load-more"]'),
			nextPage     = offset + 1,
			prevPage     = offset - 1,
			prevButton   = main_parent.find('.downloads-block-navigation button').first(),
			nextButton   = main_parent.find('.downloads-block-navigation button').last();

		link.attr('disabled', 'disabled');

		const handleObj = {
			data    : data,
			main_parent,
			offsetHolder,
			offset,
			link,
			nextPage,
			prevPage,
			prevButton,
			nextButton,
			doAction: action
		}

		dlmReportsInstance.handleSliderNavigation(handleObj);
	}
	/**
	 * HTML entities for Download's title
	 *
	 * @param string
	 * @returns {string}
	 */
	htmlEntities(string) {
		var textarea = document.getElementById("dlm_reports_decode_area");
		textarea.innerHTML = string;
		return textarea.value;

	}
	/**
	 * Get existing Downloads
	 *
	 * @returns {Promise<void>}
	 */
	async fetchDownloadsCPT() {
		const fetchedUserData = await fetch(dlmDownloadsCptApiapi);

		if (!fetchedUserData.ok) {
			throw new Error('Something went wrong! Reports response did not come OK - ' + fetchedUserData.statusText);
		}

		dlmReportsInstance.downloads = await fetchedUserData.json();

		// Fetch our users and the logs. Do this first so that we query for users we have data.
		dlmReportsInstance.fetchUserData();
	}
	/**
	 * Get download object based on ID
	 * @param $id
	 * @returns {{total: number}}
	 */
	altGetDownloadCPT($id) {

		let download = null;
		if (Array.isArray(dlmReportsInstance.mostDownloaded)) {
			download = dlmReportsInstance.mostDownloaded.filter((item) => {
				return item.id === $id;
			}, 0)[0];
		}

		jQuery(document).trigger('dlm_download_cpt', [dlmReportsInstance, download]);

		return download;
	}
}

Copyright © 2019 by b0y-101