Skip to content
Extraits de code Groupes Projets
datatable.js 44,5 ko
Newer Older
  • Learn to ignore specific revisions
  • robocoder's avatar
    robocoder a validé
    /*!
    
    robocoder's avatar
    robocoder a validé
     * Piwik - Web Analytics
     *
     * @link http://piwik.org
     * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
     */
    
    
    //-----------------------------------------------------------------------------
    
    //								DataTable
    
    //-----------------------------------------------------------------------------
    //A list of all our DataTables
    //Test if the object have already been initialized (multiple includes)
    if(typeof dataTables == "undefined")
    
    //Prototype of the DataTable object
    dataTable.prototype =
    {
    	//initialisation function
    	init: function(workingDivId, domElem)
    	{
    		if(typeof domElem == "undefined")
    		{
    			domElem = $('#'+workingDivId);
    		}
    		
    		this.workingDivId = workingDivId;
    
    		this.loadedSubDataTable = {};
    
    		this.bindEventsAndApplyStyle(domElem);
    		this.initialized = true;
    	},
    	
    	//function triggered when user click on column sort
    	onClickSort: function(domElem)
    	{
    		var self = this;
    		var newColumnToSort = $(domElem).attr('id');
    		// we lookup if the column to sort was already this one, if it is the case then we switch from desc <-> asc 
    		if(self.param.filter_sort_column == newColumnToSort)
    		{
    			// toggle the sorted order
    			if(this.param.filter_sort_order == 'asc')
    			{
    				self.param.filter_sort_order = 'desc';
    			}
    			else
    			{
    				self.param.filter_sort_order = 'asc';
    			}
    		}
    		self.param.filter_offset = 0; 
    		self.param.filter_sort_column = newColumnToSort;
    		self.reloadAjaxDataTable();
    	},
    	
    
    	setGraphedColumn: function( columnName )
    	{
    		this.param.columns = columnName;
    	},
    	
    
    	//Reset DataTable filters (used before a reload or view change)
    	resetAllFilters: function()
    	{
    		var self = this;
    		var FiltersToRestore = new Array();
    		filters = [ 
    			'filter_column', 
    			'filter_pattern', 
    			'filter_column_recursive', 
    			'filter_pattern_recursive', 
    
    			'enable_filter_excludelowpop',
    
    			'columns',
    			'flat',
    			'include_aggregate_rows'
    
    		];
    		
    		for(var key in filters)
    		{
    			var value = filters[key];
    			FiltersToRestore[value] = self.param[value];
    			delete self.param[value];
    		}
    		
    		return FiltersToRestore;
    	},
    	
    	//Restores the filters to the values given in the array in parameters
    	restoreAllFilters: function(FiltersToRestore)
    	{
    		var self = this;
    		for(key in FiltersToRestore)
    		{
    			self.param[key] = FiltersToRestore[key];
    		}
    	},
    	
    	//Translate string parameters to javascript builtins
    	//'true' -> true, 'false' -> false
    	//it simplifies condition tests in the code
    	cleanParams: function()
    	{
    		var self = this;
    		for(var key in self.param)
    		{
    			if(self.param[key] == 'true') self.param[key]=true;
    			if(self.param[key] == 'false') self.param[key]=false;
    		}
    	},
    		
    	// Returns the standard Ajax request object used by the Jquery .ajax method
    	buildAjaxRequest: function(callbackSuccess)
    	{
    		var self = this;
    		
    		//prepare the ajax request
    		var ajaxRequest = 
    		{
    			type: 'GET',
    
    			error: piwikHelper.ajaxHandleError,		// Callback when the request fails
    
    			success: callbackSuccess,	// Callback when the request succeeds
    			data: new Object
    		};
    		
    		//Extract the configuration from the datatable and pass it to the API
    		for(var key in self.param)
    		{
    
    			if(typeof self.param[key] != "undefined")
    
    				ajaxRequest.data[key] = self.param[key];
    		}
    		
    		return ajaxRequest;
    	},
    	
    	// Function called to trigger the AJAX request 
    	// The ajax request contains the function callback to trigger if the request is successful or failed
    
    mattpiwik's avatar
    mattpiwik a validé
    	// displayLoading = false When we don't want to display the Loading... DIV .loadingPiwik
    
    	// for example when the script add a Loading... it self and doesn't want to display the generic Loading
    	reloadAjaxDataTable: function(displayLoading, callbackSuccess)
    	{
    		var self = this;
    		
    		if (typeof displayLoading == "undefined") 
    		{
    			displayLoading = true;
    		}
    		if (typeof callbackSuccess == "undefined") 
    		{
    			callbackSuccess = self.dataTableLoaded;
    		}
    		
    		if(displayLoading)
    		{
    
    mattpiwik's avatar
    mattpiwik a validé
    			$('#'+self.workingDivId+' .loadingPiwik').last().css('display','block');
    
    		var container = $('#'+self.workingDivId+' .piwik-graph');
    		piwikHelper.queueAjaxRequest($.ajax(self.buildAjaxRequest(function(response) {
    			container.trigger('piwikDestroyPlot');
    
    			container.off('piwikDestroyPlot');
    
    	},
    			
    	
    	// Function called when the AJAX request is successful
    	// it looks for the ID of the response and replace the very same ID 
    	// in the current page with the AJAX response
    	dataTableLoaded: function(response)
    	{
    		var content = $(response);
    		var idToReplace = $(content).attr('id');
    		var dataTableSel = $('#'+idToReplace);
    
    mattpiwik's avatar
     
    mattpiwik a validé
    		// if the current dataTable is located inside another datatable
    
    		table = $(content).parents('table.dataTable');
    		if(dataTableSel.parents('.dataTable').is('table'))
    		{
    			// we add class to the table so that we can give a different style to the subtable
    
    			$(content).find('table.dataTable').addClass('subDataTable');
    
    robocoder's avatar
    robocoder a validé
    			$(content).find('.dataTableFeatures').addClass('subDataTable');
    
    			
    			//we force the initialisation of subdatatables
    
    robocoder's avatar
    robocoder a validé
    			dataTableSel.html( $(content) );
    
    			dataTableSel.find('object').remove();
    
    robocoder's avatar
    robocoder a validé
    			dataTableSel.html( $(content) );
    
    		piwikHelper.lazyScrollTo(dataTableSel[0], 400);
    
    	},	
    		
    			
    	/* This method is triggered when a new DIV is loaded, which happens
    		- at the first loading of the page
    		- after any AJAX loading of a DataTable
    		
    	This method basically add features to the DataTable, 
    		- such as column sorting, searching in the rows, displaying Next / Previous links, etc.
    		- add styles to the cells and rows (odd / even styles)
    		- modify some rows to add images if a span img is found, or add a link if a span urlLink is found
    			or truncate the labels when they are too big
    		- bind new events onclick / hover / etc. to trigger AJAX requests, 
    			nice hovertip boxes for truncated cells
    	*/
    	bindEventsAndApplyStyle: function(domElem)
    	{
    		var self = this;
    		self.cleanParams();
    		self.handleSort(domElem);
    
    		self.handleSearchBox(domElem);
    		self.handleOffsetInformation(domElem);
    		self.handleExportBox(domElem);
    		self.applyCosmetics(domElem);
    		self.handleSubDataTable(domElem);
    
    		self.handleConfigurationBox(domElem);
    
    		self.handleColumnDocumentation(domElem);
    		self.handleReportDocumentation(domElem);
    
    		self.handleRowActions(domElem);
    
    	
    	handleLimit: function(domElem)
    	{
    		var self = this;
    
    mattpiwik's avatar
    mattpiwik a validé
    		if( typeof self.parentId != "undefined" && self.parentId != '')
    
    mattpiwik's avatar
    mattpiwik a validé
    		{
    			// no limit selector for subtables 
    
    			$('.limitSelection', domElem).remove();
    
    mattpiwik's avatar
    mattpiwik a validé
    			return;
    		}
    
    		
    		$('.limitSelection', domElem).append('<div><span>'+self.param.filter_limit+'</span></div><ul></ul>');
    
    mattpiwik's avatar
    mattpiwik a validé
    		if(self.param.viewDataTable == 'table' || self.param.viewDataTable == 'tableAllColumns' || self.param.viewDataTable == 'tableGoals' || self.param.viewDataTable == 'ecommerceOrder' || self.param.viewDataTable == 'ecommerceAbandonedCart') {
    
    			$('.limitSelection ul', domElem).hide();
    
    mattpiwik's avatar
    mattpiwik a validé
    			var numbers = [5, 10, 25, 50, 100, 250, 500];
    
    			for(var i=0; i<numbers.length; i++) {
    				$('.limitSelection ul', domElem).append('<li value="'+numbers[i]+'"><span>'+numbers[i]+'</span></li>');
    			}
    			$('.limitSelection ul li:last', domElem).addClass('last');
    			if(self.param.totalRows > 0) {
    				$('.limitSelection div', domElem).on('click', function(){
    					$('.limitSelection ul', domElem).toggle();
    					$('.limitSelection', domElem).toggleClass('visible');
    				});
    				$('.limitSelection ul li', domElem).on('click', function(event){
    					var limit = parseInt($(event.target).text());
    					$('.limitSelection', domElem).removeClass('visible');
    					$('.limitSelection ul', domElem).hide();
    					if(limit != self.param.filter_limit) {
    						self.param.filter_limit = limit;
    
    mattpiwik's avatar
    mattpiwik a validé
    						// Hack for Visitor Log to not pass the maxIdVisit parameter when limit is changed
    						delete self.param.maxIdVisit;
    
    						$('.limitSelection>div>span', domElem).text(self.param.filter_limit);
    						self.reloadAjaxDataTable();
    						self.notifyWidgetParametersChange(domElem, {'filter_limit': self.param.filter_limit});
    					}
    				});
    				$('body').on('mouseup',function(e){ 
    					if(!$(e.target).parents('.limitSelection').length && !$(e.target).is('.limitSelection')) {
    						$('.limitSelection', domElem).removeClass('visible');
    						$('.limitSelection ul', domElem).hide();
    					}
    				});
    			} else {
    				$('.limitSelection', domElem).toggleClass('disabled');
    			}
    		} else {
    			$('.limitSelection', domElem).hide();
    		}
    	},
    	
    
    	// if sorting the columns is enabled, when clicking on a column, 
    	// - if this column was already the one used for sorting, we revert the order desc<->asc
    	// - we send the ajax request with the new sorting information
    	handleSort: function(domElem)
    	{
    		var self = this;
    		if( self.param.enable_sort )
    		{
    			$('.sortable', domElem).click( 
    				function()
    				{
    
    			if (self.param.filter_sort_column != '')
    
    				// are we in a subdatatable?
    				var currentIsSubDataTable = $(domElem).parent().hasClass('cellSubDataTable');
    				
    				var prefixSortIcon = ''; 
    				if(currentIsSubDataTable)
    				{
    					prefixSortIcon = '_subtable_';
    				}
    				var imageSortWidth = 16;
    				var imageSortHeight = 16;
    				// we change the style of the column currently used as sort column
    				// adding an image and the class columnSorted to the TD
    				$(".sortable#"+self.param.filter_sort_column+' #thDIV', domElem).parent()
    					.addClass('columnSorted')
    					.prepend('<div id="sortIconContainer"><img id="sortIcon" width="'+imageSortWidth+'" height="'+imageSortHeight+'" src="themes/default/images/sort'+prefixSortIcon+ self.param.filter_sort_order+'.png" /></div>');
    
    	},
    	
    	//behaviour for the DataTable 'search box'
    	handleSearchBox: function(domElem, callbackSuccess)
    	{
    		var self = this;
    
    mattpiwik's avatar
     
    mattpiwik a validé
    	
    		var currentPattern = self.param.filter_pattern;
    		if(typeof self.param.filter_pattern != "undefined"
    
    			&& self.param.filter_pattern.length > 0)
    
    mattpiwik's avatar
     
    mattpiwik a validé
    		{
    			currentPattern = self.param.filter_pattern;
    		}
    		else if(typeof self.param.filter_pattern_recursive != "undefined"
    
    			&& self.param.filter_pattern_recursive.length > 0)
    
    mattpiwik's avatar
     
    mattpiwik a validé
    		{
    			currentPattern = self.param.filter_pattern_recursive;
    		}
    		else
    		{
    			currentPattern = '';
    		}
    
    mattpiwik's avatar
    mattpiwik a validé
    		currentPattern = piwikHelper.htmlDecode(currentPattern);
    
    mattpiwik's avatar
     
    mattpiwik a validé
    		
    
    robocoder's avatar
    robocoder a validé
    		$('.dataTableSearchPattern', domElem)
    
    mattpiwik's avatar
     
    mattpiwik a validé
    			.show()
    			.each(function(){
    				// when enter is pressed in the input field we submit the form
    				$('#keyword', this)
    
    mattpiwik's avatar
     
    mattpiwik a validé
    						function(e)
    
    mattpiwik's avatar
     
    mattpiwik a validé
    								$(this).siblings(':submit').submit(); 
    
    mattpiwik's avatar
     
    mattpiwik a validé
    						} 
    					)
    					.val(currentPattern)
    				;
    				
    				$(':submit', this).submit( 
    					function()
    
    mattpiwik's avatar
     
    mattpiwik a validé
    						var keyword = $(this).siblings('#keyword').val();
    						self.param.filter_offset = 0; 
    
    mattpiwik's avatar
     
    mattpiwik a validé
    						if(self.param.search_recursive)
    						{
    							self.param.filter_column_recursive = 'label';
    							self.param.filter_pattern_recursive = keyword;
    						}
    						else
    						{
    							self.param.filter_column = 'label';
    							self.param.filter_pattern = keyword;
    						}
    						self.reloadAjaxDataTable(true, callbackSuccess);
    
    mattpiwik's avatar
     
    mattpiwik a validé
    				);
    
    mattpiwik's avatar
     
    mattpiwik a validé
    				$(':submit', this)
    					.click( function(){ $(this).submit(); })
    				;
    
    				// in the case there is a searched keyword we display the RESET image
    				if(currentPattern)
    				{
    					var target = this;
    					var clearImg = $('<span style="position: relative;">\
    
    							<img src="plugins/CoreHome/templates/images/reset_search.png" style="position: absolute; top: 4px; left: -15px; cursor: pointer; display: inline;" title="Clear" />\
    
    mattpiwik's avatar
     
    mattpiwik a validé
    							</span>')
    						.click( function() {
    							$('#keyword', target).val('');
    							$(':submit', target).submit();
    						});
    					$('#keyword',this).after(clearImg);
    					
    				}
    			}
    		);
    
    	},
    	
    	//behaviour for '< prev' 'next >' links and page count
    	handleOffsetInformation: function(domElem)
    	{
    		var self = this;
    		
    
    robocoder's avatar
    robocoder a validé
    		$('.dataTablePages', domElem).each(
    
    mattpiwik's avatar
     
    mattpiwik a validé
    			function(){
    				var offset = 1+Number(self.param.filter_offset);
    				var offsetEnd = Number(self.param.filter_offset) + Number(self.param.filter_limit);
    				var totalRows = Number(self.param.totalRows);
    				offsetEndDisp = offsetEnd;
    
    				if(offsetEnd > totalRows) offsetEndDisp = totalRows;
    				
    				// only show this string if there is some rows in the datatable
    				if(totalRows != 0)
    				{
    
    					var str = sprintf(_pk_translate('CoreHome_PageOf_js'),offset + '-' + offsetEndDisp,totalRows);
    
    mattpiwik's avatar
     
    mattpiwik a validé
    					$(this).text(str);
    
    mattpiwik's avatar
     
    mattpiwik a validé
    			}
    		);
    		
    		// Display the next link if the total Rows is greater than the current end row
    
    robocoder's avatar
    robocoder a validé
    		$('.dataTableNext', domElem)
    
    mattpiwik's avatar
     
    mattpiwik a validé
    			.each(function(){
    				var offsetEnd = Number(self.param.filter_offset) 
    									+ Number(self.param.filter_limit);
    				var totalRows = Number(self.param.totalRows);
    				if(offsetEnd < totalRows)
    				{
    					$(this).css('display','inline');
    				}
    			})
    			// bind the click event to trigger the ajax request with the new offset
    			.click(function(){
    
    mattpiwik's avatar
     
    mattpiwik a validé
    				self.param.filter_offset = Number(self.param.filter_offset) + Number(self.param.filter_limit); 
    				self.reloadAjaxDataTable();
    			})
    		;
    		
    		// Display the previous link if the current offset is not zero
    
    robocoder's avatar
    robocoder a validé
    		$('.dataTablePrevious', domElem)
    
    mattpiwik's avatar
     
    mattpiwik a validé
    			.each(function(){
    					var offset = 1+Number(self.param.filter_offset);
    					if(offset != 1)
    
    mattpiwik's avatar
     
    mattpiwik a validé
    				}
    			)
    			// bind the click event to trigger the ajax request with the new offset
    			// take care of the negative offset, we setup 0 
    			.click(
    				function(){
    
    mattpiwik's avatar
     
    mattpiwik a validé
    					var offset = Number(self.param.filter_offset) - Number(self.param.filter_limit);
    					if(offset < 0) { offset = 0; }
    
    mattpiwik's avatar
    mattpiwik a validé
    					self.param.filter_offset = offset;
    					self.param.previous = 1;
    
    mattpiwik's avatar
     
    mattpiwik a validé
    				}
    			);
    
    	// DataTable view box (simple table, all columns table, Goals table, pie graph, tag cloud, graph, ...)
    
    	handleExportBox: function(domElem)
    	{
    		var self = this;
    
    mattpiwik's avatar
     
    mattpiwik a validé
    		{
    			// no view box for subtables
    
    mattpiwik's avatar
     
    mattpiwik a validé
    		
    
    		// When the (+) image is hovered, the export buttons are displayed 
    
    robocoder's avatar
    robocoder a validé
    		$('.dataTableFooterIconsShow', domElem)
    
    			.show()
    			.hover( function() {
    					$(this).fadeOut('slow');
    
    robocoder's avatar
    robocoder a validé
    					$('.exportToFormatIcons', $(this).parent()).show('slow');
    
    Jonas Heitzer's avatar
    Jonas Heitzer a validé
    		//footer arrow position element name
    
    		self.jsViewDataTable=$('.dataTableFooterWrap', domElem).attr('var');
    
    		$('.tableAllColumnsSwitch a', domElem)
    
    			.show()
    			.click(
    				function(){
    					// we only reset the limit filter, in case switch to table view from cloud view where limit is custom set to 30
    
    					// this value is stored in config file General->datatable_default_limit but this is more an edge case so ok to set it to 10
    
    					
    					self.setActiveIcon(this, domElem);
    					
    					var viewDataTable = $(this).attr('format');
    					self.param.viewDataTable = viewDataTable;
    					
    					//self.resetAllFilters();
    					
    					// when switching to display simple table, do not exclude low pop by default
    					delete self.param.enable_filter_excludelowpop; 
    
    mattpiwik's avatar
    mattpiwik a validé
    					delete self.param.filter_sort_column;
    					delete self.param.filter_sort_order;
    
    					self.reloadAjaxDataTable();
    
    					self.notifyWidgetParametersChange($(this), {viewDataTable: self.param.viewDataTable});
    
    Jonas Heitzer's avatar
    Jonas Heitzer a validé
    		//handle Graph View icons
    
    		$('.tableGraphViews a', domElem)
    			.click(function(){
    				var viewDataTable = $(this).attr('format');
    				self.setActiveIcon(this, domElem);
    
    BeezyT's avatar
    BeezyT a validé
    				
    				var filters = self.resetAllFilters();
    				self.param.flat = filters.flat;
    
    				self.param.columns = filters.columns;
    
    BeezyT's avatar
    BeezyT a validé
    				
    
    				self.param.viewDataTable = viewDataTable;
    				self.reloadAjaxDataTable();
    
    				self.notifyWidgetParametersChange($(this), {viewDataTable: self.param.viewDataTable});
    
    			});
    		
    		//Graph icon Collapsed functionality
    		self.currentGraphViewIcon=0;
    		self.graphViewEnabled=0;
    
    Jonas Heitzer's avatar
    Jonas Heitzer a validé
    		self.graphViewStartingThreads=0;
    		self.graphViewStartingKeep=false; //show keep flag
    		
    		//define collapsed icons
    
    		$('.tableGraphCollapsed a', domElem)
    			.each(function(i){
    				if(self.jsViewDataTable==$(this).attr('var')){
    					self.currentGraphViewIcon=i;
    					self.graphViewEnabled=true;
    
    			})
    			.each(function(i){
    				if(self.currentGraphViewIcon!=i) $(this).hide();
    			});
    		
    		$('.tableGraphCollapsed', domElem).hover(
    			function(){
    
    Jonas Heitzer's avatar
    Jonas Heitzer a validé
    				//Graph icon onmouseover
    				if(self.graphViewStartingThreads>0) return self.graphViewStartingKeep=true; //exit if animation is not finished
    
    				$(this).addClass('tableIconsGroupActive');
    				$('a', this).each(function(i){
    
    Jonas Heitzer's avatar
    Jonas Heitzer a validé
    					if(self.currentGraphViewIcon!=i || self.graphViewEnabled){
    						self.graphViewStartingThreads++;
    					}
    					if(self.currentGraphViewIcon!=i){
    						//show other icons
    						$(this).show('fast', function(){self.graphViewStartingThreads--});
    					}
    					else if (self.graphViewEnabled){
    						//set footer arrow position
    						$('.dataTableFooterActiveItem', domElem).animate({left:$(this).parent().position().left+i*(this.offsetWidth+1)}, "fast", function(){self.graphViewStartingThreads--});
    					}
    
    				});
    				self.exportToFormatHide(domElem);
    			},
    			function(){
    
    Jonas Heitzer's avatar
    Jonas Heitzer a validé
    				//Graph icon onmouseout
    				if(self.graphViewStartingKeep) return self.graphViewStartingKeep=false; //exit while icons animate
    
    Jonas Heitzer's avatar
    Jonas Heitzer a validé
    					if(self.currentGraphViewIcon!=i){
    						//hide other icons
    						$(this).hide('fast');
    					}
    					else if (self.graphViewEnabled){
    						//set footer arrow position
    						$('.dataTableFooterActiveItem', domElem).animate({left:$(this).parent().position().left}, "fast");
    					}
    
    				});
    				$(this).removeClass('tableIconsGroupActive');
    			}
    
    Jonas Heitzer's avatar
    Jonas Heitzer a validé
    		//handle exportToFormat icons
    
    		self.exportToFormat=null;
    		$('.exportToFormatIcons a', domElem).click(function(){
    			self.exportToFormat={};
    			self.exportToFormat.lastActiveIcon=self.setActiveIcon(this, domElem);
    			self.exportToFormat.target=$(this).parent().siblings('.exportToFormatItems').show('fast');
    			self.exportToFormat.obj=$(this).hide();
    
    Jonas Heitzer's avatar
    Jonas Heitzer a validé
    		//close exportToFormat onClickOutside
    
    		$('body').on('mouseup',function(e){
    
    Jonas Heitzer's avatar
    Jonas Heitzer a validé
    				if(self.exportToFormat){
    					self.exportToFormatHide(domElem);
    				}
    		});
    
    mattpiwik's avatar
    mattpiwik a validé
    		$('.exportToFormatItems a', domElem)
    			// prevent click jacking attacks by dynamically adding the token auth when the link is clicked
    			.click( function() {
    				$(this).attr('href', function() { 
    					return $(this).attr('href') +'&token_auth='+piwik.token_auth;
    				})
    			})
    			.attr( 'href', function(){
    
    				var format = $(this).attr('format');
    				var method = $(this).attr('methodToCall');
    				var filter_limit = $(this).attr('filter_limit');
    
    mattpiwik's avatar
    mattpiwik a validé
    				var segment = self.param.segment;
    
    				var label = self.param.label;
    
    				var param_date = self.param.date;
    				var date = $(this).attr('date');
    				if(typeof date != 'undefined') {
    					param_date = date; 
    				}
    
    				var period = self.param.period;
    				
    				// RSS does not work for period=range
    				if(format == 'RSS'
    						&& self.param.period == 'range') {
    					period = 'day';
    				}
    
    				var str = 'index.php?module=API'
    
    						+'&method='+method
    						+'&format='+format
    						+'&idSite='+self.param.idSite
    
    mattpiwik's avatar
    mattpiwik a validé
    						+ ( typeof self.param.filter_pattern != "undefined" ? '&filter_pattern=' + self.param.filter_pattern : '')
    
    BeezyT's avatar
    BeezyT a validé
    						+ ( typeof self.param.filter_pattern_recursive != "undefined" ? '&filter_pattern_recursive=' + self.param.filter_pattern_recursive : '');
    				
    				if (typeof self.param.flat != "undefined" && self.param.flat) {
    					str += '&flat=1';
    					if (typeof self.param.include_aggregate_rows != "undefined" && self.param.include_aggregate_rows) {
    
    BeezyT's avatar
    BeezyT a validé
    						str += '&include_aggregate_rows=1';
    
    BeezyT's avatar
    BeezyT a validé
    					}
    				} else {
    					str += '&expanded=1';
    
    				}
    				if (format == 'CSV' || format == 'TSV' || format == 'RSS') {
    					str += '&translateColumnNames=1&language='+piwik.language;
    
    mattpiwik's avatar
    mattpiwik a validé
    				if(typeof segment != 'undefined') {
    					str += '&segment='+segment;
    				}
    
    				if(typeof idGoal != 'undefined'
    					&& idGoal != '-1') {
    
    				if(filter_limit)
    
    					str += '&filter_limit='+filter_limit;
    				}
    				if(label)
    				{
    					str += '&label='+encodeURIComponent(label);
    
    mattpiwik's avatar
    mattpiwik a validé
    		// Initialize arrow footer to correct icon
    
    		$('.dataTableFooterWrap a.tableIcon', domElem).each(function(){
    			if(self.jsViewDataTable==$(this).attr('var')) self.setActiveIcon(this, domElem); 
    		});
    		
    	},	
    	
    
    BeezyT's avatar
    BeezyT a validé
    	exportToFormatHide: function(domElem, noAnimation)
    
    	{
    		var self=this;
    		if(self.exportToFormat){
    			self.setActiveIcon(self.exportToFormat.lastActiveIcon, domElem);
    
    BeezyT's avatar
    BeezyT a validé
    			var animationSpeed = noAnimation ? 0 : 'fast';
    			self.exportToFormat.target.hide(animationSpeed);
    			self.exportToFormat.obj.show(animationSpeed);
    
    	handleConfigurationBox: function(domElem, callbackSuccess)
    
    		if (typeof self.parentId != "undefined" && self.parentId != '')
    		{
    			// no manipulation when loading subtables 
    			return;
    		}
    		
    		if ((typeof self.numberOfSubtables == 'undefined' || self.numberOfSubtables == 0)
    			&& (typeof self.param.flat == 'undefined' || self.param.flat != 1))
    		{
    			// if there are no subtables, remove the flatten action
    			$('.dataTableFlatten', domElem).parent().remove();
    		}
    		
    		var ul = $('div.tableConfiguration ul', domElem);
    		
    
    BeezyT's avatar
    BeezyT a validé
    		if (ul.find('li').size() == 0 ||
    			!(self.param.viewDataTable == 'table' || self.param.viewDataTable == 'tableAllColumns'
    				|| self.param.viewDataTable == 'tableGoals'))
    
    BeezyT's avatar
    BeezyT a validé
    			// hide the icon when there are no actions available or we're not in a table view
    
    			$('div.tableConfiguration', domElem).remove();
    			return;
    		}
    		
    
    		var icon = $('a.tableConfigurationIcon', domElem);
    		icon.click(function() { return false; });
    		var iconHighlighted = false;
    		
    
    		ul.find('li:first').addClass('first');
    		ul.find('li:last').addClass('last');
    		ul.prepend('<li class="firstDummy"></li>');
    		
    		// open and close the box
    
    BeezyT's avatar
    BeezyT a validé
    		var open = function() {
    			self.exportToFormatHide(domElem, true);
    
    BeezyT's avatar
    BeezyT a validé
    		var close = function() {
    
    			icon.css('opacity', icon.hasClass('highlighted') ? .85 : .6);
    
    BeezyT's avatar
    BeezyT a validé
    		$('div.tableConfiguration', domElem).hover(open, close);
    
    		
    		var generateClickCallback = function(paramName, callbackAfterToggle)
    		{
    			return function()
    			{
    				close();
    				self.param[paramName] = 1 - self.param[paramName];
    				self.param.filter_offset = 0;
    				if (callbackAfterToggle) callbackAfterToggle();
    				self.reloadAjaxDataTable(true, callbackSuccess);
    
                    var data = {};
                    data[paramName]    = self.param[paramName];
                    self.notifyWidgetParametersChange(domElem, data);
    
    		var getText = function(text, addDefault)
    
    BeezyT's avatar
    BeezyT a validé
    		{
    			text = _pk_translate(text);
    			if (text.indexOf('%s') > 0)
    			{
    				text = text.replace('%s', '<br /><span class="action">&raquo; ');
    
    				if (addDefault) text += ' (' + _pk_translate('CoreHome_Default_js') + ')';
    
    BeezyT's avatar
    BeezyT a validé
    				text += '</span>';
    			}
    			return text;
    		};
    		
    
    		var setText = function(el, paramName, textA, textB)
    		{
    			if (typeof self.param[paramName] != 'undefined' && self.param[paramName] == 1)
    			{
    
    				$(el).html(getText(textA, true));
    				iconHighlighted = true;
    
    BeezyT's avatar
    BeezyT a validé
    				$(el).html(getText(textB));
    
    		$('.dataTableExcludeLowPopulation', domElem)
    
    			.each(function()
    			{
    				// Set the text, either "Exclude low pop" or "Include all"
    				if(typeof self.param.enable_filter_excludelowpop == 'undefined')
    
    					self.param.enable_filter_excludelowpop = 0;
    				}
    				if(Number(self.param.enable_filter_excludelowpop) != 0)
    
    					string = getText('CoreHome_IncludeRowsWithLowPopulation_js', true);
    
    					self.param.enable_filter_excludelowpop = 1;
    
    BeezyT's avatar
    BeezyT a validé
    					string = getText('CoreHome_ExcludeRowsWithLowPopulation_js');
    
    					self.param.enable_filter_excludelowpop = 0;
    				}
    				$(this).html(string);
    			})
    			.click( generateClickCallback('enable_filter_excludelowpop') );
    		
    		// handle flatten
    		$('.dataTableFlatten', domElem)
    			.each( function() {
    				setText(this, 'flat', 'CoreHome_UnFlattenDataTable_js', 'CoreHome_FlattenDataTable_js');
    			})
    			.click( generateClickCallback('flat') );
    		
    		$('.dataTableIncludeAggregateRows', domElem)
    			.each( function() {
    				setText(this, 'include_aggregate_rows', 'CoreHome_DataTableExcludeAggregateRows_js',
    					'CoreHome_DataTableIncludeAggregateRows_js');
    			})
    			.click( generateClickCallback('include_aggregate_rows', function() {
    				if (self.param.include_aggregate_rows == 1)
    				{
    					// when including aggregate rows is enabled, we remove the sorting
    					// this way, the aggregate rows appear directly before their children
    					self.param.filter_sort_column = '';
    
                        self.notifyWidgetParametersChange(domElem, {filter_sort_column: ''});
    
    		// handle highlighted icon
    		if (iconHighlighted)
    		{
    
    		
    		// fix a css bug of ie7
    		if (document.all && !window.opera && window.XMLHttpRequest)
    		{
    			window.setTimeout(function() {
    				open();
    				var width = 0;
    				ul.find('li').each(function() {
    					width = Math.max(width, $(this).width()); 
    				}).width(width);
    				close();
    			}, 400);
    		}
    
    Jonas Heitzer's avatar
    Jonas Heitzer a validé
    	//footer arrow position handler
    
    	setActiveIcon: function(obj, domElem)
    	{	
    		if(!obj) return false;
    
    Jonas Heitzer's avatar
    Jonas Heitzer a validé
    		
    
    		var lastActiveIcon=this.lastActiveIcon;
    
    Jonas Heitzer's avatar
    Jonas Heitzer a validé
    		
    		if(lastActiveIcon){
    			$(lastActiveIcon).removeClass("activeIcon");
    		}
    		
    
    		$(obj).addClass("activeIcon");
    		this.lastActiveIcon=obj;
    
    Jonas Heitzer's avatar
    Jonas Heitzer a validé
    		
    
    		var target=$('.dataTableFooterActiveItem', domElem);
    
    Jonas Heitzer's avatar
    Jonas Heitzer a validé
    		
    
    		//set arrow position with delay (for ajax widget loading)
    		setTimeout(function(){
    
    Jonas Heitzer's avatar
    Jonas Heitzer a validé
    			target.css({left:$(obj).position().left});
    
    Jonas Heitzer's avatar
    Jonas Heitzer a validé
    		
    
    	// Tell parent widget that the parameters of this table was updated,
    
    	notifyWidgetParametersChange: function(domWidget, parameters)
    	{
    
            var widget = $(domWidget).parents('[widgetId]');
            // trigger setParameters event on base element
            widget.trigger('setParameters', parameters);
    
    	truncate: function(domElemToTruncate, truncationOffset)
    
    BeezyT's avatar
    BeezyT a validé
    		domElemToTruncate = $(domElemToTruncate);
    		
    		if (typeof domElemToTruncate.data('originalText') != 'undefined')
    		{
    			// truncate only once. otherwise, the tooltip will show the truncated text as well.
    			return;
    		}
    		
    		// make the original text (before truncation) available for others. 
    		// the .truncate plugins adds a title to the dom element but the .tooltip 
    		// plugin removes that again. 
    		domElemToTruncate.data('originalText', domElemToTruncate.text());
    		
    		if (typeof truncationOffset == 'undefined')
    		{
    
    			truncationOffset = 0;
    		}
    
    mattpiwik's avatar
    mattpiwik a validé
    		var truncationLimit = 50;
    
    mattpiwik's avatar
    mattpiwik a validé
    		
    
    BeezyT's avatar
    BeezyT a validé
    		if (typeof self.param.idSubtable == 'undefined'
    			&& self.param.viewDataTable == 'tableAllColumns')
    		{
    			// when showing all columns in a subtable, space is restricted
    			truncationLimit = 25; 
    		}
    
    BeezyT's avatar
    BeezyT a validé
    		truncationLimit += truncationOffset;
    
    		domElemToTruncate.truncate(truncationLimit);
    
    BeezyT's avatar
    BeezyT a validé
    		
    		$('.truncated', domElemToTruncate).tooltip();
    
    	},
    
    	//Apply some miscelleaneous style to the DataTable
    	applyCosmetics: function(domElem)
    	{
    		var self = this;
    
    robocoder's avatar
    robocoder a validé
    
    
    		// Add some styles on the cells even/odd
    		// label (first column of a data row) or not
    		$("th:first-child", domElem).addClass('label');
    		$("td:first-child:odd", domElem).addClass('label labeleven');
    		$("td:first-child:even", domElem).addClass('label labelodd');
    		$("tr:odd td", domElem).slice(1).addClass('columnodd');
    		$("tr:even td", domElem).slice(1).addClass('columneven');
    
    
    		$('td span.label', domElem).each(function(){ self.truncate($(this)); } );
    
    		
    	},
     	
     	//behaviour for 'nested DataTable' (DataTable loaded on a click on a row)
     	handleSubDataTable: function(domElem)
    	{
    		var self = this;
    		// When the TR has a subDataTable class it means that this row has a link to a subDataTable
    
    		this.numberOfSubtables = $('tr.subDataTable', domElem)
    
    			.click( 
    			function()
    			{
    				// get the idSubTable
    				var idSubTable = $(this).attr('id');
    				var divIdToReplaceWithSubTable = 'subDataTable_'+idSubTable;
    				
    				// if the subDataTable is not already loaded
    				if (typeof self.loadedSubDataTable[divIdToReplaceWithSubTable] == "undefined")
    				{
    					var numberOfColumns = $(this).children().length;
    					
    					// at the end of the query it will replace the ID matching the new HTML table #ID
    					// we need to create this ID first
    
    					$(this).after( 
    					'<tr>'+
    						'<td colspan="'+numberOfColumns+'" class="cellSubDataTable">'+
    							'<div id="'+divIdToReplaceWithSubTable+'">'+
    
    mattpiwik's avatar
    mattpiwik a validé
    								'<span class="loadingPiwik" style="display:inline"><img src="themes/default/images/loading-blue.gif" />'+ _pk_translate('General_Loading_js') +'</span>'+
    
    					
    					var savedActionVariable = self.param.action;
    					
    					// reset all the filters from the Parent table
    					var filtersToRestore = self.resetAllFilters();
    
    					// do not ignore the exclude low population click
    					self.param.enable_filter_excludelowpop = filtersToRestore.enable_filter_excludelowpop;
    
    					self.param.action = self.param.controllerActionCalledWhenRequestSubTable;
    
    					self.reloadAjaxDataTable(false);
    					
    					self.param.action = savedActionVariable;
    					delete self.param.idSubtable;
    					self.restoreAllFilters(filtersToRestore);
    					
    					self.loadedSubDataTable[divIdToReplaceWithSubTable] = true;
    					
    					$(this).next().toggle();
    
    					
    					// when "loading..." is displayed, hide actions
    					// repositioning after loading is not easily possible
    					$(this).find('div.dataTableRowActions').hide();
    
    				self.repositionRowActions($(this));
    
    	},
    	
    	// tooltip for column documentation
     	handleColumnDocumentation: function(domElem)
    	{
    		if ($('#dashboard').size() > 0) {
    			// don't display column documentation in dashboard
    			// it causes trouble in full screen view
    			return;
    		}