/**
 * jQuery UI : advanced searcher
 *
 * @author  Marc Mignonsin
 * @version 2010-12-07
 *
 */

(function( $ ) {
	$.widget("ui.advancedsearcher", {
		// default options
		options: {	
			formID : '',
			script : '',
			controller : null
		},
		
		_create: function() {
			//window.log('ui.advancedsearcher._create', this.options);
			
			// --- internal configuration ---
			
			this.headerClass = 'panel-header';
			
			this.mainSearchTextID = 'search-operand1-0';
			this.formFieldClass = 'form-field';
			this.searchFieldClass = 'search-field';
			this.templateIDPrefix = 'template';
			this.searchOperatorandsClass = 'search-operatorands';
			this.searchOperandsClass = 'search-operands';
			this.searchOperatorClass = 'search-operator';
			this.formFieldsClass = 'form-fields';
			this.depFieldsGroupClass = 'dependency-fields-group';
			this.messageID = 'message';
			this.searchFieldIDPrefix = 'search-field';
			this.searchOperatorandsIDPrefix = 'search-operatorands';
			this.depTemplateIDPrefix = 'template-dependency';
			this.formFieldIDPrefix = 'form-field';
			this.isDatePickerClass = 'isDatepicker';
			this.categGroupTemplateIDPrefix = 'template-category-group';
			this.categTemplateIDPrefix = 'template-category';
			this.searchGroupClass = 'search-group';
			this.addCategButtonClass = 'add-categ';
			this.removeButtonClass = 'remove';
			this.searchOperatorIDPrefix = 'search-operator';
			this.searchOperand1IDPrefix = 'search-operand1';
			this.searchOperand2IDPrefix = 'search-operand2';
			this.searchOperandsIDPrefix = 'search-operands';
			this.inputErrorClass = 'input-error';
			
			// --- state variables & init ---
			
			this.searchScript = this.options.script + '.php';
			this.updateScript = $(this.options.controller).panelslider('getSystemToolsURL')+'element/call-element-operation.php?op=searchUpdate';
			this.loadInfoScript = $(this.options.controller).panelslider('getSystemToolsURL')+'search/load-search-info.php';
			this.loadFilterScript = $(this.options.controller).panelslider('getSystemToolsURL')+'filter/load.php';
			this.saveFilterScript = $(this.options.controller).panelslider('getSystemToolsURL')+'filter/save.php';
			this.loadTemplateScript = $(this.options.controller).panelslider('getSystemToolsURL')+'search/load-search-template.php';

			// the ui element is the panel itself
			this.uiPanel = this.element;
			this.panelID = this.uiPanel.attr('id');			
			this.uiHeader = this.uiPanel.find('.'+this.headerClass);
			
			this.currentCritID = 0;
			this.lastData = {};

			// search request
			this.searchRequest = new AjaxRequest(this.panelID, this.options.controller, this.searchScript, 'POST', 'html');
			this.searchRequest.onSuccess(delegate(this, function(dataReceived, textStatus) {
				// prevent from being called when aborted
				if (dataReceived != '')
				{
					this._onResultsReceived(dataReceived);
				}
			}));
			this.searchRequest.onError(delegate(this, function (XMLHttpRequest) {
				window.log(XMLHttpRequest);
				var errorMsg = XMLHttpRequest.message;
				this._onResultsReceived(errorMsg);
			}));
		},
		
		// internal : called @ creation and when the search panel is reloaded
		_init : function()
		{
			//window.log('ui.advancedsearcher._init', arguments);
			this._abortSearchRequest(); // just in case... (panel reloading)
			
			this.uiSearchForm = this.uiPanel.find('#'+this.options.formID);
			
			this.resetButton = this.uiSearchForm.find('input#reset-button');
			this.searchButton = this.uiSearchForm.find('input#search-button');
			this.editButton = this.uiSearchForm.find('input#edit-button');
			this.saveButton = this.uiSearchForm.find('input#save-button');
			
			/* form buttons */
			this.resetButton.click(delegate(this, function(){ 
				return this._resetForm();
			}));
			
			this.uiSearchForm.submit(delegate(this, function() {
	            return this.submit();
	        }));
			
			this.editButton.click(delegate(this, function() {
	            return this._search4Edit();
	        }));
				
			/* events */
			this._bindSearchEvents();
		},
		
		// internal : events 
		_bindSearchEvents: function() {
			//window.log('ui.advancedsearcher._bindSearchEvents', arguments);			
	        this.uiPanel.bind('search-loadpage', delegate (this, function(eventObject, page){
	            this._gotoPage(page);	            
	        }));
	        
			this.uiPanel.bind('search-loaddisplay', delegate(this, function(eventObject, srcElem, filterID){
	            this._loadDisplayFilter(srcElem, filterID);	            
	        }));
			
			this.uiPanel.bind('search-applydisplay', delegate(this, function(eventObject, config){
	            this._applyDisplayFilter(config);	            
	        }));
			
			this.uiPanel.bind('search-savedisplay', delegate(this, function(eventObject, config){
	            this._saveDisplayFilter(config);	            
	        }));
			
			this.uiPanel.bind('search-savequery', delegate(this, function(eventObject, config){
	            this._saveQueryFilter(config);	            
	        }));
			
			this.uiPanel.bind('search-displayselections', delegate(this, function(eventObject, page, displaySelection){
	            this._displaySelections(page, displaySelection); 
	        }));
		},
		
		// internal : reset form
		_resetForm : function()
		{
			//window.log('ui.advancedsearcher._resetForm', arguments);			
			this.uiSearchForm.find('.'+this.formFieldClass+':visible:gt(0)').remove();
		    this.uiSearchForm.find('input#'+this.mainSearchTextID).val('');
		    this.uiSearchForm.find('select.'+this.searchFieldClass+' option').show();
		    
			this._updateLayout();

			return false;
		},
		
		// internal : layout
		_updateLayout : function()
		{
			//window.log('ui.advancedsearcher._updateLayout', arguments);
			this.uiSearchForm.find('.'+this.formFieldClass+':visible').not('.'+this.depFieldsGroupClass+' .'+this.formFieldClass).each(function(i){
				var newClass = (i & 1) ? 'even': 'odd';
				$(this).removeClass('even odd').addClass(newClass);
			});

			this._displayMessage(false);

			this.uiPanel.trigger('panel-layout');
		},
		
		// internal : warning message
		_displayMessage : function(display)
		{
			//window.log('ui.advancedsearcher._displayMessage', arguments);
			if (display == true)
			{
				this.uiSearchForm.find('#'+this.messageID).show();
			}
			else
			{
				this.uiSearchForm.find('#'+this.messageID).hide();
			}
		},
		
		_addStaticParms : function(data2Send)
		{
			//window.log('ui.advancedsearcher._addStaticParms', arguments);
			data2Send.panelID = this.panelID;
			data2Send.tabSessionID = $(this.options.controller).panelslider('getTabSessionID');
		    
			data2Send.editor = this.uiSearchForm.find('input#editor:hidden').val();
			data2Send.app = this.uiSearchForm.find('input#app:hidden').val();
			data2Send.module = this.uiSearchForm.find('input#module:hidden').val();
		    
			return data2Send;
		},
		
		_parseSelection : function(data2Send)
		{
			//window.log('ui.advancedsearcher._parseSelection', arguments);
			var selectedItemsList = '';

			var items = this.uiPanel.find('#datagrid tbody input:checkbox[name=results[]]');
			var checkedItems = items.filter(':checked');
			var prevSelectedItems = this.uiPanel.find('#datagrid input[name=selections[]]:hidden');

			checkedItems.each(function() {
				var currentVal = $(this).val();
				selectedItemsList += currentVal + ',';
			});

			prevSelectedItems.each(function() {
				var currentVal = $(this).val();

				if (!items.is('[value='+currentVal+']'))
				{
					selectedItemsList += currentVal + ',';
				}
			});

			if (selectedItemsList != '') data2Send.selectedItems = selectedItemsList;
			data2Send.displaySelection = this.uiSearchForm.find('input#displaySelection:hidden').val();
			data2Send.lastHits = this.uiSearchForm.find('input#lastHits:hidden').val();
			
			return data2Send;
		},
		
		_parseProfile : function(data2Send, sortField)
		{
			//window.log('ui.advancedsearcher._parseProfile', arguments);
			var columnsList = '';
			var columnsElems = this.uiSearchForm.find('input[name=columns[]]:hidden');
			var columnsCount = columnsElems.length;

			columnsElems.each(function(i) {
					columnsList += $(this).val();
					if (i < (columnsCount - 1)) columnsList += ',';
				}
			);

			if (columnsList != '') data2Send.columns = columnsList;

			/* *** */

			var sortInput = this.uiSearchForm.find('input#sort:hidden');
			var orderInput = this.uiSearchForm.find('input#order:hidden');

			if (sortField)
			{
				var newOrder = 'ascending';

				if (sortInput.val() == sortField)
				{
					newOrder = (orderInput.val() == 'ascending') ? 'descending' : 'ascending'; // toggle
				}

				// update hidden form inputs
				orderInput.val(newOrder); // on a blue monday...
				sortInput.val(sortField);
			}

			data2Send.order = orderInput.val();
			data2Send.sort = sortInput.val();

			data2Send.display = this.uiSearchForm.find('input#display:hidden').val();
			
			/* *** */
			
			var filterID = this.uiSearchForm.find('input#filterID:hidden').val();
			if (filterID)
			{
				data2Send.filterID = filterID;
			}
			
			return data2Send;
		},
		
		_parse : function(page, sortField)
		{
			//window.log('ui.advancedsearcher._parse', arguments);
			var data2Send = {};
			page = (page) ? page : 1;
			data2Send.page = page;
			
			data2Send = this._addStaticParms(data2Send);
			data2Send = this._parseSelection(data2Send);
			data2Send = this._parseProfile(data2Send, sortField);
			
			var emptyFields = false;
			var self = this;

			this.uiSearchForm.find('.'+this.formFieldClass+':visible :input').each(function(i)
			{
				//window.log(this);
				var name = $(this).attr('name');
				//window.log(name);
				if (name != '')
				{
					var value = $(this).val();
					//window.log(value);
					data2Send[name] = value;
					if (value != '')
					{
						$(this).removeClass(self.inputErrorClass);
					}
					else
					{
						if ($(this).attr('id') != self.mainSearchTextID) // no error if it's the default crit
						{
							emptyFields = true;
							$(this).addClass(self.inputErrorClass);
						}
					}
				}
			});
			
			this._displayMessage(emptyFields);
			
			//window.log(data2Send);			
			return data2Send;
		},
		
		// internal : generic search function, may be called from data grid headers, etc.
		_search : function(page, sortField, cutChildrenPanels)
		{
			//window.log('ui.advancedsearcher._search', arguments);
			this.resetButton.attr('disabled', 'disabled');
			this.saveButton.attr('disabled', 'disabled');
		
			if (cutChildrenPanels == true)
			{
				$(this.options.controller).panelslider('cutChildrenPanels', this.panelID);
			}

			this._abortSearchRequest(); // just in case...			
			this.uiPanel.trigger('panel-displayloader', [true, 'body']);
			
			this.lastData = this._parse(page, sortField);
			
			// use the editor/app hidden form values (allows cross-applications search)
			this.searchRequest.setScript('../../'+this.lastData.editor+'/'+this.lastData.app+'/'+this.searchScript);
			this.searchRequest.send(this.lastData);
		},
		
		// internal : submit search, called from the pages browser buttons
		_gotoPage : function(page)
		{
			//window.log('ui.advancedsearcher._gotoPage', arguments);
			this._search(page, null, false);
		},
		
		_search4Edit : function()
		{
			//window.log('ui.advancedsearcher._search4Edit', arguments);
			this.resetButton.attr('disabled', 'disabled');
			this.saveButton.attr('disabled', 'disabled');

			this._abortSearchRequest(); // just in case...
			this.uiPanel.trigger('panel-displayloader', [true, 'body']);
			
			this.lastData = this._parse(this.lastData.page, this.lastData.sortField);
			this.lastData.searchMode = 'edit';
			
			// use the editor/app hidden form values (allows cross-applications search)
			this.searchRequest.setScript('../../'+this.lastData.editor+'/'+this.lastData.app+'/'+this.searchScript);
			this.searchRequest.send(this.lastData);
			
			return false;
		},
		
		_updateResultsSet : function()
		{
			//window.log('ui.advancedsearcher._updateResultsSet', arguments);			
			var data2Send = {};
			data2Send = this._addStaticParms(data2Send);
			
			var items = this.uiPanel.find('#datagrid tbody input:checkbox[name=results[]]');
			
			var checkedItems = items.filter(':checked');			
			if (checkedItems.length == 0)
			{
				var dialog = new NotificationDialog('Warning!');
				dialog.setStatusMessage('No results have been selected.<br /><br />Please select the results you want to update.', 'Ok');
				dialog.show();
				return false;
			}
			
			$(this.options.controller).panelslider('cutChildrenPanels', this.panelID);
			
			// parse updates						
			var currentElem, key, value;
			checkedItems.each(function(){
				$(this).closest('.items').find(':input').each(function(){
					currentElem = $(this);
					key = currentElem.attr('name');
					
					if (currentElem.is(':checkbox'))
					{
						value = (currentElem.attr('checked') == true) ? '1' : '0';
					}
					else
					{
						value = currentElem.val();
					}
					
					data2Send[key] = value;
				});
			});
			
			data2Send.searchMode = 'update';
			
			var updateRequest = new AjaxRequest(this.panelID, this.options.controller, this.updateScript, 'POST', 'json');
			// display error dialog and return errors in long msgs format
			updateRequest.errorDialog(true, false);
			updateRequest.internalErrorDialog(true, false);
			updateRequest.successDialog(true, null, 'Ok', 1500);		
			updateRequest.processingDialog(true, 'Saving results set updates...');		    
			updateRequest.send(data2Send); 

			return false;
		},
		
		// internal : ...
		_onResultsReceived : function(data)
		{
			//window.log('ui.advancedsearcher._onResultsReceived');
			this.uiPanel.trigger('panel-displayloader', [false, 'body']).trigger('panel-blockcontent', ['.block-list', data]);
			
			/* options */

			this.uiPanel.find('#open-options').click(delegate(this, this._displayOptions));
			this.uiPanel.find('#close-options').click(delegate(this, this._displayOptions));
			
			/* update results set button (optional) */
			
			if (this.lastData.searchMode == 'edit')
			{
				this.uiPanel.find('input.update-results-button').click(delegate(this, function(){ 
					return this._updateResultsSet();
				}));
			}
			
			/* data grid headers */

			var self = this;
			var headers = this.uiPanel.find('#datagrid thead th.listHeader');
			headers.click(function(){
				return self._search(self.lastData.page, $(this).attr('id'), true);
			});

			var newSortClass = (this.lastData.order == 'ascending') ? 'headerSortAsc' : 'headerSortDesc';
			headers.filter('[id='+this.lastData.sort+']').addClass(newSortClass);
			
			/* selections + hidden inputs form updates */
			
			this.uiPanel.find('#datagrid thead th.checkboxcolumn input:checkbox').click(function(){
				self._toggleSelection(this, true); 
			});
			
			this.uiPanel.find('#datagrid tbody td.checkboxcolumn input:checkbox').click(function(){
				self._toggleSelection(this); 
			});
			
			if ((this.lastData.displaySelection == 'yes') && (this.lastData.searchMode != 'edit'))
			{
				this.uiPanel.find('#datagrid input:checkbox').attr('checked', true);
			}
						
			// update param for _displaySelections()
			var hitsCount =  this.uiPanel.find('.hits .lasthits-count').eq(0).text(); // we need only the top or the bottom element value
			this.uiSearchForm.find('input#lastHits:hidden').val(hitsCount);
			
			this.uiSearchForm.find('input#page:hidden').val(this.lastData.page);

			/* layout */

			this.resetButton.removeAttr('disabled');
			this.searchButton.removeAttr('disabled');
			this.saveButton.removeAttr('disabled');

			this.uiPanel.trigger('panel-layout');
		},
		
		// internal : ...
		_displayOptions : function()
		{
			//window.log('ui.advancedsearcher._displayOptions', arguments);
			this._displayMessage(false); // cosmetics

			var body = this.uiPanel.find('.panel-body');
			if (body.attr('scrollTop') > 0)
			{
				body.animate({
					scrollTop: 0
				}, 500, 'easeOutExpo', delegate(this, this._toggleOptions));
			}
			else
			{
				this._toggleOptions();
			}

			return false;
		},
		
		_toggleOptions : function()
		{
			//window.log('ui.advancedsearcher._toggleOptions', arguments);
			this.uiPanel.find('#options-navigation').toggle("fast", delegate(this, function(){
				this.uiPanel.find('#open-options').toggle();
				this.uiPanel.find('#close-options').toggle();
				this._updateLayout();
			}));

			return false;
		},
		
		_toggleSelection : function(srcElem, toggleAll)
		{
			//window.log('ui.advancedsearcher._toggleAllSelections', arguments);	
			var c = $(srcElem).attr('checked');
			var inc;
			
			if (toggleAll === true)
			{
				var checkedItems = this.uiPanel.find('#datagrid tbody input:checkbox[name=results[]]');
				inc = (c == true) ? +checkedItems.length : -checkedItems.length;
				var adjustInc = (c == true) ? -1 : +1;
				
				checkedItems.each(function(){
					if ($(this).attr('checked') == c)
					{
						inc += adjustInc;
					}
					$(this).attr('checked', c);
				});				
			}
			else
			{
				inc = (c == true) ? +1 : -1;
			}			
			
			/* count update */

			var countElem = this.uiPanel.find('.hits .selected-count').eq(0); // we need only the top or the bottom element value
			var newCount = parseInt(countElem.text()) + inc;
			if (newCount > 0)
			{
				var link = $('<a href="#" class="selection-link" title="Display selections"></a>').click(delegate(this, function(){
					this._displaySelections(1, true); 
				}));
				this.uiPanel.find('.hits .selection-text').wrap(link);
			}
			else
			{
				newCount = 0;
				var selectionLinkElems =  this.uiPanel.find('.hits .selection-link');
				var html = selectionLinkElems.eq(0).html(); // we need only the top or the bottom element value
				selectionLinkElems.replaceWith(html); // update both (top & bottom) counts
			}

			this.uiPanel.find('.hits .selected-count').text(newCount); // update both (top & bottom) counts
		},
				
		_displaySelections : function(page, displaySelection)
		{
			//window.log('ui.advancedsearcher._displaySelections', arguments);
			var s = (displaySelection === true) ? 'yes' : 'no';
			this.uiSearchForm.find('input#displaySelection:hidden').val(s);
			return this._search(page, null, false);
		},
		
		// internal : add dependency search group
		_addDependencySearch : function(lineID, dependencyType, dependencyTarget)
		{
			//window.log('ui.advancedsearcher._addDependencySearch', arguments);			
			var templateID = this.depTemplateIDPrefix+'-' + dependencyType.replace(/:/, '__') + '-' + dependencyTarget.replace(/:/, '__');			
			var template = this.uiSearchForm.find('#'+templateID);
			
			if (template.length == 0)
			{
				this._loadTemplateAndAddDepCrit(lineID, dependencyType, templateID);
			}
			else
			{
				this._cloneTemplateAndAddDepCrit(lineID, template);
			}
		},
		
		// internal : add category search group
		_addCategorySearch : function(lineID, categName, srcElem)
		{
			//window.log('ui.advancedsearcher._addCategorySearch', arguments);
	        var templateID = this.categGroupTemplateIDPrefix + '-' + categName;
	        var template = this.uiSearchForm.find('#'+templateID);
	        
			if (template.length == 0)
			{
				this._loadTemplateAndAddCategCrit(lineID, categName, srcElem, templateID);
			}
			else
			{
				this._cloneTemplateAndAddCategCrit(lineID, categName, srcElem, template);
			}
		},
		
		// internal : load operator and operands inputs
		_loadOperatorands : function(lineID, fieldType, operatorSelected)
		{
			//window.log('ui.advancedsearcher._loadOperatorands', arguments);
			var removeButton = this.uiSearchForm.find('#'+this.formFieldIDPrefix+'-'+lineID+' a.'+this.removeButtonClass);
			removeButton.hide();

			this.resetButton.attr('disabled', 'disabled');
			this.searchButton.attr('disabled', 'disabled');
			this.editButton.attr('disabled', 'disabled');
			this.saveButton.attr('disabled', 'disabled');

			this._displayMessage(false);
			
			var operatorandsElem = this.uiSearchForm.find('#'+this.searchOperatorandsIDPrefix+'-'+lineID);
			operatorandsElem.html('<div class="ajax-loader" />');
			
			/* *** */

			var params = {
				lineID : lineID,
				fieldType : fieldType,
				defaultOperator : operatorSelected,
		        panelID : this.panelID // for js-init-datepicker
			};

			$.ajax({
				type : 'POST',
				url : this.loadInfoScript+'?request=operatorands',
				data : params,

				success : delegate(this, function(dataReceived, textStatus) {
					operatorandsElem.html(dataReceived);

					this.resetButton.removeAttr('disabled');
					this.searchButton.removeAttr('disabled');
					this.editButton.removeAttr('disabled');
					this.saveButton.removeAttr('disabled');

					removeButton.show();
				}),

				error : delegate(this, function (XMLHttpRequest, textStatus, errorThrown) {					
					window.log(XMLHttpRequest);
					operatorandsElem.html('');
					var errorMsg = 'AJAX error : '+XMLHttpRequest.statusText+' ('+XMLHttpRequest.status+')';
					this._onResultsReceived(errorMsg);
				})
			});
		},
		
		// internal : load operands inputs
		_loadOperands : function(lineID, fieldType, operator, operand1, operand2)
		{
			//window.log('ui.advancedsearcher._loadOperatorands', arguments);
			var removeButton = this.uiSearchForm.find('#'+this.formFieldIDPrefix+'-'+lineID+' a.'+this.removeButtonClass);
			removeButton.hide();

			this.resetButton.attr('disabled', 'disabled');
			this.searchButton.attr('disabled', 'disabled');
			this.editButton.attr('disabled', 'disabled');
			this.saveButton.attr('disabled', 'disabled');

			this._displayMessage(false);
			
			var operandsElem = this.uiSearchForm.find('#'+this.searchOperandsIDPrefix+'-'+lineID);
			operandsElem.html('<div class="ajax-loader" />');

			var params = {
				lineID : lineID,
				fieldType : fieldType,
				operator : operator,
				operand1 : operand1,
				operand2 : operand2,
		        panelID : this.panelID // for js-init-datepicker
			};

			$.ajax({
				type : 'POST',
				url : this.loadInfoScript+'?request=operands',
				data : params,

				success : delegate(this, function(dataReceived, textStatus) {
					operandsElem.html(dataReceived);

					this.resetButton.removeAttr('disabled');
					this.searchButton.removeAttr('disabled');
					this.editButton.removeAttr('disabled');
					this.saveButton.removeAttr('disabled');

					removeButton.show();
				}),

				error : delegate(this, function (XMLHttpRequest, textStatus, errorThrown) {
					window.log(XMLHttpRequest);
					operandsElem.html('');
					var errorMsg = 'AJAX error : '+XMLHttpRequest.statusText+' ('+XMLHttpRequest.status+')';					
					this._onResultsReceived(errorMsg);
				})
			});
		},
		
		// internal : ...
		_loadDisplayFilter : function(srcElem, filterID)
		{
			//window.log('ui.advancedsearcher._loadDisplayFilter', arguments);
			this.resetButton.attr('disabled', 'disabled');
			this.searchButton.attr('disabled', 'disabled');
			this.editButton.attr('disabled', 'disabled');
			this.saveButton.attr('disabled', 'disabled');
			
			var infoElem = $(srcElem).find('.info');
			var labelElem = infoElem.find('.actions-label');
			if (labelElem.length != 0) labelElem = infoElem;
			infoElem.find('*:hidden:not(.actions-label)').show().addClass('toggle');
			
			var params = {
				filterID : filterID
			};

			$.ajax({
				type : 'POST',
				url : this.loadFilterScript,
				data : params,
				dataType : 'json',

				success : delegate(this, function(response) {
					this.resetButton.removeAttr('disabled');
					this.searchButton.removeAttr('disabled');
					this.editButton.removeAttr('disabled');
					this.saveButton.removeAttr('disabled');
					
					if (response.status == 'success')
					{
						infoElem.find('.toggle').hide().removeClass('toggle');
						this._applyDisplayFilter(response);
					}
					else
					{
						infoElem.html('<span class="text-error">'+response.message+'</span>');
					}
				}),

				error : delegate(this, function (XMLHttpRequest, textStatus, errorThrown) {
					window.log(XMLHttpRequest);
					infoElem.html('<span class="text-error">error ('+XMLHttpRequest.status+') !</span>');
					
					this.resetButton.removeAttr('disabled');
					this.searchButton.removeAttr('disabled');
					this.editButton.removeAttr('disabled');
					this.saveButton.removeAttr('disabled');
				})
			});
		},
		
		// internal : ...
		_applyDisplayFilter : function(config)
		{
			//window.log('ui.advancedsearcher._applyDisplayFilter', arguments);
			this.uiSearchForm.find('input[name=columns[]]:hidden').remove();

			var columnsCount = config.columns.length;
			for (var i=0; i<columnsCount; i++)
			{
				this.uiSearchForm.append("<input type='hidden' name='columns[]' value='"+config.columns[i]+"' />");
			}

			this.uiSearchForm.find('input#sort:hidden').val(config.sort);
			this.uiSearchForm.find('input#order:hidden').val(config.order);
			this.uiSearchForm.find('input#display:hidden').val(config.display);

			//this.lastData.page = (config.page) ? config.page : this.lastData.page;

			this._search(this.lastData.page, null, false);
		},
		
		// internal : ...
		_saveDisplayFilter : function(config)
		{
			//window.log('ui.advancedsearcher._saveDisplayFilter', arguments);
			config.columns = config.columns.join(','); // adjustment for PHP
			
			var saveRequest = new AjaxRequest(this.panelID, this.options.controller, this.saveFilterScript, 'POST', 'json');
			
			// display error dialog and return errors in long msgs format
			saveRequest.errorDialog(true, false);
			saveRequest.internalErrorDialog(true, false);
			saveRequest.successDialog(true, 'Display configuration successfully saved!', 'Ok', 1500);
			saveRequest.processingDialog(true, 'Saving display configuration...');
			
			saveRequest.send(config);
		},
		
		// internal : ...
		_saveQueryFilter : function(config)
		{
			//window.log('ui.advancedsearcher._saveQueryFilter', arguments);
			var data2Send = this._parse(this.lastData.page, this.lastData.sortField);

			data2Send['filter-name']	= config['filter-name'];
			data2Send['filter-shared']  = config['filter-shared'];
			data2Send['filter-count']   = config['filter-count'];
			data2Send['filter-query']   = config['filter-query'];
			data2Send['filter-display'] = config['filter-display'];
			data2Send['filter-search']	= config['filter-search'];
			if (config['filter-overwrite'])
			{
				data2Send['filter-overwrite'] = config['filter-overwrite'];
			}			
			
			var saveRequest = new AjaxRequest(this.panelID, this.options.controller, this.saveFilterScript, 'POST', 'json');
			
			// display error dialog and return errors in long msgs format
			saveRequest.errorDialog(true, false);
			saveRequest.internalErrorDialog(true, false);
			saveRequest.successDialog(true, 'Search filter successfully saved!', 'Ok', 1500);
			saveRequest.processingDialog(true, 'Saving search filter...');
			
			saveRequest.onSuccess(delegate(this, function(response){
				var filterInput = this.uiSearchForm.find('input#filterID:hidden');
				var filterID = filterInput.val();
				filterInput.val(response.ID);
				
				this.uiPanel.find('.panel-title-bar .title').html(config['filter-name']);
				
				if (filterID != '')
				{
					$(this.options.controller).panelslider('reloadParentPanel', this.panelID);
				}
			}));
			
			saveRequest.send(data2Send);
		},
		
		// internal : ...
		_loadTemplateAndAddCrit : function(srcElem, dependencyType) {
			//window.log('ui.advancedsearcher._loadTemplateAndAddCrit', arguments);
			this.uiPanel.trigger('panel-displayloader', [true, 'header']);
			
			var params = {};
			params = this._addStaticParms(params);
			
			if (dependencyType != '')
			{
				params.dependencyType = dependencyType;
			}
			
			$.ajax({
				type : 'POST',
				url : this.loadTemplateScript,
				data : params,

				success : delegate(this, function(response) {	
					this.uiPanel.trigger('panel-displayloader', [false, 'header']);
					if ((response.indexOf(' error</b>') == -1) &&	// PHP
							(response.indexOf('Error: ') == -1) && 	// dev : custom
							(response.indexOf('ERROR: ') == -1))	// dev : custom
					{
						var formFieldsContainer = this.uiSearchForm.find('.'+this.formFieldsClass);
						var template = $(response);
						template.prependTo(formFieldsContainer);			
						this._cloneTemplateAndAddCrit(srcElem, template);
					}
					else
					{
						this._onResultsReceived(response);
					}
				}),

				error : delegate(this, function (XMLHttpRequest, textStatus, errorThrown) {					
					window.log(XMLHttpRequest);
					this.uiPanel.trigger('panel-displayloader', [false, 'header']);
					var errorMsg = 'AJAX error : '+XMLHttpRequest.statusText+' ('+XMLHttpRequest.status+')';
					this._onResultsReceived(errorMsg);
				})
			});
		},
		
		// internal : ...
		_cloneTemplateAndAddCrit : function(srcElem, template)
		{
			//window.log('ui.advancedsearcher._cloneTemplateAndAddCrit', arguments);
			var newCritLine = template.clone();

			var newID = ++this.currentCritID;
		   	newCritLine.attr('id', this.formFieldIDPrefix+'-'+this.currentCritID);

			var operatorandsDiv = newCritLine.find('.'+this.searchOperatorandsClass).eq(0);
			var newName = operatorandsDiv.attr('class') + '-' + newID;
			operatorandsDiv.attr('id', newName).attr('name', newName);

			var operandsDiv = newCritLine.find('.'+this.searchOperandsClass).eq(0);
			newName = operandsDiv.attr('class') + '-' + newID;
			operandsDiv.attr('id', newName).attr('name', newName);
			
			/* *** */

			var isDatepicker;

			newCritLine.find(':input').each(function()
			{
				if (isDatepicker = $(this).hasClass(this.isDatePickerClass))
				{
					$(this).removeClass(this.isDatePickerClass);
				}

				var newName = $(this).attr('class') + '-' + newID;
				$(this).attr('id', newName).attr('name', newName);

				// init this new cloned instance
				if (isDatepicker)
				{
					$(this).initDatepicker();
				}
			});

			/* *** */

			newCritLine.find('select#'+this.searchFieldClass+'-'+newID).change(delegate(this, function(){
				this.selectOperatorands(newID);
			}));

			newCritLine.find('select#'+this.searchOperatorClass+'-'+newID).change(delegate(this, function(){
				this.selectOperands(newID);
			}));

			var container = (srcElem) ? $(srcElem).parent().parent() : this.uiSearchForm.find('.'+this.formFieldsClass);
			container.append(newCritLine);
			
			newCritLine.show();
			
			this._updateLayout();			
			return false;
		},
		
		// internal : ...
		_loadTemplateAndAddDepCrit : function(lineID, dependencyType, templateID) {
			//window.log('ui.advancedsearcher._loadTemplateAndAddDepCrit', arguments);
			this.uiPanel.trigger('panel-displayloader', [true, 'header']);
			
			var params = {};
			params = this._addStaticParms(params);

			if (dependencyType != '')
			{
				params.dependencyType = dependencyType;
			}
			
			$.ajax({
				type : 'POST',
				url : this.loadTemplateScript,
				data : params,

				success : delegate(this, function(response) {	
					this.uiPanel.trigger('panel-displayloader', [false, 'header']);
					if ((response.indexOf(' error</b>') == -1) &&	// PHP
							(response.indexOf('Error: ') == -1) && 	// dev : custom
							(response.indexOf('ERROR: ') == -1))	// dev : custom
					{
						var formFieldsContainer = this.uiSearchForm.find('.'+this.formFieldsClass);
						$(response).prependTo(formFieldsContainer);
						var template = this.uiSearchForm.find('#'+templateID);
						this._cloneTemplateAndAddDepCrit(lineID, template);
					}
					else
					{
						this._onResultsReceived(response);
					}
				}),

				error : delegate(this, function (XMLHttpRequest, textStatus, errorThrown) {					
					window.log(XMLHttpRequest);
					this.uiPanel.trigger('panel-displayloader', [false, 'header']);
					var errorMsg = 'AJAX error : '+XMLHttpRequest.statusText+' ('+XMLHttpRequest.status+')';
					this._onResultsReceived(errorMsg);
				})
			});
		},
		
		// internal : ...
		_cloneTemplateAndAddDepCrit : function(lineID, template)
		{
			//window.log('ui.advancedsearcher._cloneTemplateAndAddDepCrit', arguments);
			var newCritLine = template.clone();

			this.currentCritID++;
		   	newCritLine.attr('id', this.formFieldIDPrefix+'-'+this.currentCritID);
			var newID = ++this.currentCritID;

			/* *** */

			var container = newCritLine.find('#'+this.formFieldIDPrefix+'-').eq(0);
			var newName = container.attr('id') + newID;
			container.attr('id', newName);

			var operatorandsDiv = newCritLine.find('.'+this.searchOperatorandsClass).eq(0);
			var newName = operatorandsDiv.attr('class') + '-' + newID;
			operatorandsDiv.attr('id', newName).attr('name', newName);

			var operandsDiv = newCritLine.find('.'+this.searchOperandsClass).eq(0);
			newName = operandsDiv.attr('class') + '-' + newID;
			operandsDiv.attr('id', newName).attr('name', newName);
			
			/* *** */

			var isDatepicker;

			newCritLine.find(':input').each(function()
			{
				if (isDatepicker = $(this).hasClass(this.isDatePickerClass))
				{
					$(this).removeClass(this.isDatePickerClass);
				}

				var newName = $(this).attr('class') + '-' + newID;
				$(this).attr('id', newName).attr('name', newName);

				// init this new cloned instance
				if (isDatepicker)
				{
					$(this).initDatepicker();
				}
			});
			
			/* *** */

			newCritLine.find('select#'+this.searchFieldClass+'-'+newID).change(delegate(this, function(){
				this.selectOperatorands(newID);
			}));

			newCritLine.find('select#'+this.searchOperatorClass+'-'+newID).change(delegate(this, function(){
				this.selectOperands(newID);
			}));

	        /* *** */

	        this.uiSearchForm.find('#'+this.formFieldIDPrefix+'-'+lineID).replaceWith(newCritLine);
			newCritLine.show();
			
			this._updateLayout();
		},
		
		// internal : ...
		_loadTemplateAndAddCategCrit : function(lineID, categName, srcElem, templateID) {
			//window.log('ui.advancedsearcher._loadTemplateAndAddCategCrit', arguments);
			this.uiPanel.trigger('panel-displayloader', [true, 'header']);
			
			var params = {};
			params = this._addStaticParms(params);
			params.category = categName;
			
			$.ajax({
				type : 'POST',
				url : this.loadTemplateScript,
				data : params,

				success : delegate(this, function(response) {	
					this.uiPanel.trigger('panel-displayloader', [false, 'header']);
					if ((response.indexOf(' error</b>') == -1) &&	// PHP
							(response.indexOf('Error: ') == -1) && 	// dev : custom
							(response.indexOf('ERROR: ') == -1))	// dev : custom
					{
						var formFieldsContainer = this.uiSearchForm.find('.'+this.formFieldsClass);
						$(response).prependTo(formFieldsContainer);
						var template = this.uiSearchForm.find('#'+templateID);
						this._cloneTemplateAndAddCategCrit(lineID, categName, srcElem, template);
					}
					else
					{
						this._onResultsReceived(response);
					}
				}),

				error : delegate(this, function (XMLHttpRequest, textStatus, errorThrown) {					
					window.log(XMLHttpRequest);
					this.uiPanel.trigger('panel-displayloader', [false, 'header']);
					var errorMsg = 'AJAX error : '+XMLHttpRequest.statusText+' ('+XMLHttpRequest.status+')';
					this._onResultsReceived(errorMsg);
				})
			});
		},
		
		// internal : ...
		_cloneTemplateAndAddCategCrit : function(lineID, categName, srcElem, template)
		{
			//window.log('ui.advancedsearcher._cloneTemplateAndAddCategCrit', arguments);
	        var newCritLine = template.clone();

	        this.currentCritID++;
	       	newCritLine.attr('id','form-field-'+this.currentCritID);
	        var newID = ++this.currentCritID;

	        /* *** */

	        var container = newCritLine.find('#'+this.formFieldIDPrefix+'-').eq(0);
	        var newName = container.attr('id') + newID;
	        container.attr('id', newName);

			var operatorandsDiv = newCritLine.find('.'+this.searchOperatorandsClass).eq(0);
			var newName = operatorandsDiv.attr('class') + '-' + newID;
			operatorandsDiv.attr('id', newName).attr('name', newName);

			var operandsDiv = newCritLine.find('.'+this.searchOperandsClass).eq(0);
			newName = operandsDiv.attr('class') + '-' + newID;
			operandsDiv.attr('id', newName).attr('name', newName);

	        newCritLine.find(':input').each(function()
	        {
	            var newName = $(this).attr('class') + '-' + newID;
	            $(this).attr('id', newName).attr('name', newName);
	        });
	        
	        newCritLine.find('input.'+this.searchGroupClass+':hidden').each(function()
	        {
	            var newval = $(this).val() + newID;
	            $(this).val(newval);
	        });

	        /* *** */
	        
	        var addButton = newCritLine.find('a.'+this.addCategButtonClass);
	        addButton.click(delegate(this, function(){
	            this._addCategorySearch(newID, categName, addButton);
	        }));
	        
	        if (srcElem)
	        {
	        	srcElem.parent().parent().append(newCritLine);
	        }
	        else
	        {
	        	this.uiSearchForm.find('#'+this.formFieldIDPrefix+'-'+lineID).replaceWith(newCritLine);
	        }	        
	        newCritLine.show();
	        
	        this._updateLayout();
		},
		
		// submit search, called from the search button
		submit : function(page)
		{
			//window.log('ui.advancedsearcher.submit', arguments);			
			// remove all selections
			this.uiPanel.find('#datagrid input[name=selections[]]:hidden').remove();
			this.uiPanel.find('#datagrid input:checkbox').removeAttr('checked');
			this.uiSearchForm.find('input#displaySelection:hidden').val('no');
						
			this._search(page, null, false);
			
			return false;
		},
				
		// initialize the criterion ID (after having loaded a filter)
		initCritID : function(ID)
		{
			//window.log('ui.advancedsearcher.initCritID', arguments);
			this.currentCritID = ID;
		},
		
		// add an info search criterion
		addCrit : function(srcElem, dependencyType) {
			//window.log('ui.advancedsearcher.addCrit', arguments);
			var templateID = this.templateIDPrefix + '-' + dependencyType.replace(/:/, '__');
			var template = this.uiSearchForm.find('#'+templateID);
			
			if (template.length == 0)
			{
				this._loadTemplateAndAddCrit(srcElem, dependencyType);
			}
			else
			{
				this._cloneTemplateAndAddCrit(srcElem, template);
			}
			
			return false;
		},
		
		// remove a criterion
		removeCrit : function(removeButton)
		{
			//window.log('ui.advancedsearcher.removeCrit', arguments);
			$(removeButton).parent().remove();
			this._updateLayout();
			return false;
		},
		
		// ...
		restoreDependencySearchOption : function(dependencyType, dependencyTarget)
		{
			//window.log('ui.advancedsearcher.restoreDependencySearchOption', arguments);
		    // show this dependency choice in all drop-down lists, see selectOperatorands
			var selectValue = 'optgroup-dependency-'+dependencyType+'-'+dependencyTarget;
			this.uiSearchForm.find('select.'+this.searchFieldClass+' option[value='+selectValue+']').show();
		},
		
		// ...
		removeDependencySearch : function(removeButton, dependencyType, dependencyTarget)
		{
			//window.log('ui.advancedsearcher.removeDependencySearch', arguments);
		    this.restoreDependencySearchOption(dependencyType, dependencyTarget);
			$(removeButton).parent().remove();
			this._updateLayout();
		},
		
		// ...
		selectOperatorands : function(lineID)
		{
			//window.log('ui.advancedsearcher.selectOperatorands', arguments);
		    var selectElem = this.uiSearchForm.find('#'+this.searchFieldIDPrefix+'-'+lineID);
			var selectValue = selectElem.val();
			//window.log(selectValue);
			var infos = selectValue.split('-');
			
		    // FORMATS EXPECTED :
		        // main module:	
		        // 	[MODULE NAME]-info-[FIELD NAME]-[TYPE]
		        // 	optgroup-dependency-[DEPENDENCY NAME]-[MODULE NAME]
				// 	optgroup-category
			
		        // module in dependency:	
		        // 	dependency-[DEPENDENCY NAME]-[MODULE NAME]-[SERVICE NAME]-[FIELD NAME]-[TYPE]
		        // 	dependency-[DEPENDENCY NAME]-[MODULE NAME]-dephits

		    var requestType = infos[0];
			var fieldType;
			
			if (requestType == 'dependency')
		    {
		        var service = infos[3];
		        if (service == 'dephits')
		        {
		            //this.uiSearchForm.find('#'+this.searchOperatorandsIDPrefix+'-'+lineID).html('');
		            //return;
		        	fieldType = 'dephits';
		        }
		        else
		        {
		            fieldType = infos[5];
		        }
		    }
		    else if (requestType == 'optgroup')
		    {
		    	if (infos[1] == 'dependency')
		    	{
					var dependencyType = infos[2];
					var dependencyTarget = infos[3];
					this._addDependencySearch(lineID, dependencyType, dependencyTarget);

					// hide this dependency choice in all drop-down lists
					this.uiSearchForm.find('select.'+this.searchFieldClass+' option[value='+selectValue+']').hide();
					return;
			    }
			    else if (infos[1] == 'category')
			    {
			    	var categName = infos[2];
			    	this._addCategorySearch(lineID, categName, null);
			    	return;
			    }
		    }
		    else
		    {
		    	fieldType = infos[3];
		    }

		    this._loadOperatorands(lineID, fieldType, '');
		},

		// ...
		selectOperands : function(lineID)
		{
			//window.log('ui.advancedsearcher.selectOperands', arguments);
			var selectValue = this.uiSearchForm.find('#'+this.searchFieldIDPrefix+'-'+lineID).val();
			//window.log(selectValue);
			var infos = selectValue.split('-');

		     // FORMATS EXPECTED :
		        // main module =>
		        //  [MODULE NAME]-info-[FIELD NAME]-[TYPE]
		        //
		        // module in dependency =>
				// dependency-[DEPENDENCY NAME]-[MODULE NAME]-dephits
		        //  dependency-[DEPENDENCY NAME]-[MODULE NAME]-[SERVICE NAME]-[FIELD NAME]-[TYPE]

			var requestType = infos[0];
			var fieldType;

			if (requestType == 'dependency')
			{
				fieldType = (infos[3] == 'dephits') ? 'dephits' : infos[5];
			}
			else
			{
				fieldType = infos[3];
			}

			var operator = this.uiSearchForm.find('#'+this.searchOperatorIDPrefix+'-'+lineID).val();

			var operand1 = this.uiSearchForm.find('#'+this.searchOperand1IDPrefix+'-'+lineID);
			var operand1Value = '';
			if (operand1.length > 0) 
			{
				operand1Value = operand1.val();
				if (fieldType == 'datetime')
				{
					operand1HH = this.uiSearchForm.find('#'+this.searchOperand1IDPrefix+'_hh-'+lineID);
					operand1MM = this.uiSearchForm.find('#'+this.searchOperand1IDPrefix+'_mm-'+lineID);
					operand1SS = this.uiSearchForm.find('#'+this.searchOperand1IDPrefix+'_ss-'+lineID);
					if (operand1HH.length && operand1MM.length && operand1SS.length)
					{	
						operand1Value += ' ' + operand1HH.val() + ':' + operand1MM.val() + ':' + operand1SS.val();
					}
				}
			}

			var operand2 = this.uiSearchForm.find('#'+this.searchOperand2IDPrefix+'-'+lineID);
			var operand2Value = '';
			if (operand2.length > 0)
			{
				operand2Value = operand2.val();
				if (fieldType == 'datetime')
				{
					operand2HH = this.uiSearchForm.find('#'+this.searchOperand2IDPrefix+'_hh-'+lineID);
					operand2MM = this.uiSearchForm.find('#'+this.searchOperand2IDPrefix+'_mm-'+lineID);
					operand2SS = this.uiSearchForm.find('#'+this.searchOperand2IDPrefix+'_ss-'+lineID);
					if (operand2HH.length && operand2MM.length && operand2SS.length)
					{	
						operand2Value += ' ' + operand2HH.val() + ':' + operand2MM.val() + ':' + operand2SS.val();
					}
				}
			}

			this._loadOperands(lineID, fieldType, operator, operand1Value, operand2Value);
		},
		
		// add a category criterion
		addCategoryCrit : function(srcElem, categName)
		{
			//window.log('ui.advancedsearcher.addCategoryCrit', arguments);			
	        var templateID = '#'+this.categTemplateIDPrefix + '-' + categName;
	        var newCritLine = this.uiSearchForm.find(templateID).clone();
	        
	        var newID = ++this.currentCritID;
	       	newCritLine.attr('id', this.formFieldClass+'-'+this.currentCritID);

	        /* *** */

			var operatorandsDiv = newCritLine.find('.'+this.searchOperatorandsClass).eq(0);
			var newName = operatorandsDiv.attr('class') + '-' + newID;
			operatorandsDiv.attr('id', newName).attr('name', newName);

			var operandsDiv = newCritLine.find('.'+this.searchOperandsClass).eq(0);
			newName = operandsDiv.attr('class') + '-' + newID;
			operandsDiv.attr('id', newName).attr('name', newName);
			
			/* *** */

	        newCritLine.find(':input').each(function()
	        {
	            var newName = $(this).attr('class') + '-' + newID;
	            $(this).attr('id', newName).attr('name', newName);
	        });
	        
	        var container = $(srcElem).parent().parent();
	        
	        var newval = container.find('input.'+this.searchGroupClass+':hidden').eq(0).val();	                
	        newCritLine.find('input.'+this.searchGroupClass+':hidden').each(function()
	        {
	            $(this).val(newval);
	        });

	    	/* *** */

	        container.append(newCritLine);
	        newCritLine.show();
	        
	        this._updateLayout();	        
	        return false;
		},		
		
		// internal : abort AJAX request
		_abortSearchRequest: function() {
			//window.log('ui.advancedsearcher.abortRequest', arguments);
			if (this.searchRequest != null)
			{
				this.searchRequest.abort();
			}
		},
		
		// method : destruction
		destroy: function() {
			//window.log('ui.advancedsearcher.destroy', this.panelID);
			
			// just in case
			this._abortSearchRequest();
			delete this.searchRequest;
			this.searchRequest = null;
			
			// call default
			$.Widget.prototype.destroy.apply(this, arguments);
		}
	});
})(jQuery);