/*
@author valentin.carruesco

== options pour la balise générale <select>

 * data-hide-filters: Si renseigné, masque la recherche,
 * data-default: permet de renseigner des filtres par défaut à la recherche (au format attendu -> voir fonction php filters_set()),
 * data-label: Libellé affiché dans la partie gauche de la recherche simple,
 * data-slug : Si spécifié, la recherche devient enregistrable pour une réutilisation ultérieure,
 * data-right-scope : Correspond à la section (scope) de droit
 * data-configure : défini si l'utilisateur peut modifier de manière globale les recherches utilisateurs
 * data-only-advanced : Si l'attribut est présent, cache la recherche simple et ouvre par defaut la recherche avancée
 * data-autosearch (default: true) : si défini a false, ne lancera pas la fonction data-function automatiquement en fin de chargement du composant
 * data-urlsearch (default: true) : si défini à false, n'utilisera pas l'url pour récupérer ou définir des filtres
 * data-show-url-advanced (default: true) : si défini à false, ne dépliera pas automatiquement la recherche avancée si il y en a une dans l'url
 * data-select-from-url (default: false) : si défini à true, selectionne le filtre des raccourcis correspondant à l'URL
 * data-user-shortcut : Si renseigné, affichera les recherches enregistrées de l'utilisateur connecté
 * data-global-shortcut : Si renseigné, affichera les recherches globale enregistrées pour tous les utilisateurs

== options pour les balises <option>
 * data-operator-delete : supprime l'affiche des operateurs par défaut spécifiés en json (ex : data-operator-delete='["in","not in"]')
 * data-operator-view : customise l'affichage pour un opérateur donné (ex : data-operator-view='{"in":{"view":"tag"}}' )

 nb : La touche shift enfoncée lors de l'indentation d'un filtre forcera la création d'un nouveau groupe au lieu de chercher une fusion de groupe.
 nb 2: ce composant fonctionne avec les templates de type de filtres situés dans la page footer, exemple de type de filtre
 nb 3: les templates subcolumn référencent le plugin workflow (@TODO: à commenter par VC)

<div class="filter-value-block" data-value-type="dictionary" data-value-selector=".filter-value:last-child">
	<select class="form-control filter-operator border-0 text-primary">
		<option value="=">Égal</option>
		<option value="!=">Différent</option>
		<option value="IS NULL"  data-values="0">Non Renseigné</option>
		<option value="IS NOT NULL" data-values="0">Renseigné</option>
	</select>
	<select data-template="dictionary" data-slug="{{slug}}" data-depth="{{depth}}" class="form-control filter-value" data-disable-label></select>
</div>

data-value-type="" : permet de définir le data-type si la donnée est un composant
data-value-selector : selecteur permetant de choisir le(s) inputs représeant la valeur réele du filtre (le selecteur est scopé sur la ligne du filtre)
data-values="0" : sur l'operateur, cela permet de multiplier ou anihiler le champs de valeurs (0: supression, 1(default) un champs de valeur , n : n champs d valeur )

 */
var FilterBox = function(input,options){
	var input = $(input);
	var object = input.data('component-object');
	this.input = input;
	this.searches = {};

	//Premiere création du composant
	if(object == null){
		object = this;
		object.shift = false;

		//permet le forcing de création d'un nouveau groupe lors de l'appuis sur maj
		$(document).keydown(function(e) {
	        if((e.which | e.keyCode) != 16) return;
	        object.shift = true;
	    }).keyup(function(e) {
	        if((e.which | e.keyCode) != 16) return;
	        object.shift = false;
	    });

		object.data = options;
		object.slug = input.attr('data-slug');
		if(object.slug != null) $('.btn-search-save',object.box).removeClass('hidden');

		input.addClass('hidden');
		object.box = $('.advanced-search-box.hidden').clone().removeClass('hidden');
		object.box.attr('data-configure',input.attr('data-configure'));
		object.box.attr('data-right-scope',input.attr('data-right-scope'));
		object.box.attr('data-global-shortcut',input.attr('data-global-shortcut'));
		object.box.attr('data-user-shortcut',input.attr('data-user-shortcut'));
		object.box.attr('data-select-from-url',input.attr('data-select-from-url'));

		input.after(object.box);
		object.columns = [];
		object.tpl = $('.criterias .condition.hidden',object.box).get(0).outerHTML;
		input.data('component-object',object);
		object.columns.push({label : ' - Choix filtre - ',value:''});
		$('option',input).each(function(i,element){
			object.columns.push({
				label : $(element).text(),
				type : $(element).attr('data-filter-type'),
				value : $(element).val()
			});
		});
		if(object.columns.length<=1){
			$('.options', object.box).addClass('hidden');
			$('.simple-search .input-group-append', object.box).removeClass('hidden');
		}

		object.box.on('change','.filter-column',function(){
			var condition = $(this).closest('.condition');
			object.filter_refresh(condition,true);
		}).on('change','.filter-operator',function(){
			var condition = $(this).closest('.condition');
			object.filter_refresh(condition);
		}).on('click','.filter-option .btn-add',function(){
			var button = $(this);
			var condition = button.closest('.condition');
			object.filter_add(condition);
		}).on('click','.filter-option .btn-duplicate',function(){
			var button = $(this);
			var condition = button.closest('.condition');
			object.filter_duplicate(condition);
		}).on('keydown', function(e){
			if((e.key != '+' && e.key != '=') || !e.ctrlKey) return;
			e.preventDefault();
			var condition = $('.criterias>.condition:last-of-type:not(.hidden)');
			object.filter_add(condition);
		}).on('keyup click','.filter-keyword',function(){
			// RESET KEYWORD
			object.filter_clear_button();
		}).on('mouseover','li.condition',function(event){
			event.stopPropagation();
			event.preventDefault();
			$(this).addClass('hover');
		}).on('mouseout','li.condition',function(event){
			event.stopPropagation();
			event.preventDefault();
			$(this).removeClass('hover');
			//Indentation d'un groupe
		}).on('click',' li.condition .btn-indent',function(){
			var line = $(this).closest('li');
			//si un groupe existe AVANT la condition, on déplace celle ci en FIN de ce groupe
			if(!object.shift && line.prev('.condition').find('>ul.group').length!=0){
				group = line.prev('.condition').find('>ul.group')
				group.append(line.detach());
			//si un groupe existe APRES la condition, on déplace celle ci en DEBUT de ce groupe
			}else if(!object.shift && line.next('.condition').find('>ul.group').length!=0){
				group = line.next('.condition').find('>ul.group');
				group.prepend(line.detach());
			//si aucun groupe a proximité, on créé un nouveau groupe
			}else{
				//tweak js car jquery ne capte pas bien les selected sur les clones
				line.find(':selected').attr('selected','selected');
				var newline = line.clone();
				var newGroup = $('<ul class="group"></ul>');
				newline.prepend(newGroup);
				newline.find('.filter-column,.filter-operator,.filter-value').remove();
				line.after(newline);
				newGroup.append(line.detach());
				group = newGroup;
			}

			$(group).sortable({
				axis : 'y',
				handle: ".btn-move",
			});
		}).on('click',' li.condition .btn-unindent',function(){
			var line = $(this).closest('li.condition');
			var parent = line.closest('ul.group');
			if(parent.hasClass('criterias')) return;

			//en fonction de la position de l'item dans son group on définit si on le déplace apres ou avant le groupe d'ou on le sort.
			var middleGroup = Math.trunc($("li.condition",parent).length / 2);
			var index = $("li.condition",parent).index(line) +1 ;

			var parentLine = parent.parent();
			var current = line.detach();

			if(index <= middleGroup){
				parentLine.before(current);
			}else{
				parentLine.after(current);
			}
			//Supression auto du groupe si plus aucun condition à l'interieur
			if(parent.find('.condition').length==0) parent.parent().remove();
		}).on('click',' li .btn-delete',function(){
			var line = $(this).closest('li.condition');
			object.core_filter_delete(line);
		}).on('click','.btn-search',function(){
			object.search();
		}).on('keypress','.filter-keyword, .filter-value input',function(e){
			if(e.key != "Enter") return;
			object.search();
		//affichage du formulaire de label sur l'enregistrement d'une recherche
		}).on('click','.btn-search-save',function(e){
			e.preventDefault();
			e.stopPropagation();
			object.core_filter_save_edit();
		//Enregistrement de la recherche courante
		}).on('click','.btn-search-save-submit',function(e){
			e.preventDefault();
			e.stopPropagation();
			object.core_filter_save();
		//Définition d'une recherche pré enregistrée en global ou pas
		}).on('click','.btn-search-global',function(e){
			e.preventDefault();
			e.stopPropagation();
			object.core_filter_global_save($(this).closest('.btn-search-load'));
		//Suppression d'une recherche pré enregistrée
		}).on('click','.btn-search-load > .btn-search-delete',function(e){
			if(!confirm('Êtes-vous sûr de supprimer cette recherche définitivement ?')) return;
			e.preventDefault();
			e.stopPropagation();
			object.filter_search_remove($(this).closest('.btn-search-load').attr('data-uid'));
		//lancement d'une recherche pré enregistrée
		}).on('click','.btn-search-load',function(e){
			object.filter_search_execute($(this).attr('data-uid'));
		}).on('click','.btn-search-clean',function(){
			$.urlParam('filters','');
			$('.filter-keyword',object.box).val('');
			object.filter_clear_button();
			$('.condition:not(.hidden)',object.box).each(function(i,element){
				object.core_filter_delete($(element));
			});
			$('.condition',object.box).removeClass('error');
		});

		//ajout de la premiere condition
		object.filter_add();
		$('.advanced-button-search',object.box).click(function(){
			object.box.toggleClass('advanced');
		});

		//Preset des filtres depuis l'url si défini
		var filters = $.urlParam('filters');
		if(filters && filters!='' && (input.attr('data-urlsearch')==null || input.attr('data-urlsearch')=="true")){
			filters = JSON.parse(atob(filters));
		} else if (input.attr('data-default') && input.attr('data-default').length){
			//Preset des filtres depuis l'attribut data-default
			filters = JSON.parse(input.attr('data-default'));

			//Récupération du type de filtre pour la colonne ciblée
			$.each(filters.a, function(i, filter){
				if(filter.c==null || !filter.c.length || (filter.t!=null && filter.t.length)) return;

				var option = $('option[value="'+filter.c+'"]', object.input);
				if(!option.length){
					delete filters.a[i];
					return;
				}
				filters.a[i]['t'] = option.attr('data-filter-type');
				filters.a[i]['o'] = filter.o.toLowerCase();
			});
		} else {
			filters = "";
		}

		if(filters){
			filters.keyword = filters.k;
			filters.advanced = filters.a;
			delete filters.a;
			delete filters.k;
			filters.advanced = object.filter_rename_keys(filters.advanced,{j:'join',o:'operator',v:'value',t:'type',c:'column',s:'subcolumn',g:'group'});

	 		if(filters.keyword && filters.keyword.length) $('.filter-keyword',object.box).val(filters.keyword);

	 		if(filters.advanced.length>0){
	 			if(input.attr('data-show-url-advanced')==null || input.attr('data-show-url-advanced')=="true") $(object.box).addClass('advanced');
	 			object.filters(filters.advanced);
	 			object.box.find('.condition:not(.hidden):eq(0)').remove();
	 		}
	 	}

 		if(object.slug!=null && object.slug!='') object.filter_search_load(function(){});
 		object.load_callback();

	 	$('#search-clear',object.box).click(function(){
	 		$('.filter-keyword',object.box).val('').focus();
	 		object.filter_clear_button();
	 		object.search();
	 	});
	}else{
		//Rafraichissement et/ou récuperation du composant
		//on reload les données de l'input aux cas ou elles aient changées
		object.data = input.data();
	}

	if(object.data && object.data.onlyAdvanced!=null){
		$('.simple-search,.advanced-button-search',object.box).addClass('hidden');
		object.box.addClass('advanced');
	}
	if(object.data && object.data.label != null && object.data.label.length) $('.simple-search .data-search-label',object.box).html(object.data.label);
	if(object.data && object.data.hideFilters != null) setTimeout(function(){object.box.addClass('hidden');}, 0);

	Object.assign(this, object);
}

FilterBox.prototype.load_callback = function(){
	if(this.data && this.data.function && window[this.data.function] && (!this.data.hasOwnProperty('autosearch') || this.data.autosearch==true))
		window[this.data.function]();
};

//Suppression d'une recherche
FilterBox.prototype.filter_search_remove = function(uid){
	var object = this;

	$.action({
		action: 'core_filter_delete',
		slug: object.slug,
		uid: uid,
		global: $('.btn-search-load[data-uid="'+uid+'"]',object.box).attr('data-global')
	},function(response){
		if(response.message != null)
			$.message('info',response.message);
		object.filter_search_load();
	});
};

FilterBox.prototype.filter_clear_button = function(){
	var object = this;
	var keyword = $('.filter-keyword',object.box);
	var searchLabel = keyword.prev('.input-group-prepend');
	var clear = $('#search-clear',object.box);

	//On se base sur la valeur left car en fct de recherche
	//simple/avancée le right n'est pas fiable à 100%
	if(keyword.val().length){
		clear.removeClass('hidden');
		setTimeout(function(){
			clear.css({
				left: (searchLabel.outerWidth() + keyword.outerWidth() - 20),
				opacity: 1
			});
		}, 50);
	} else {
		clear.css({opacity: 0});
		setTimeout(function(){
			clear.addClass('hidden');
		}, 150);
	}
};

//Enregistrement d'une recherche (edition nom + options)
FilterBox.prototype.core_filter_save_edit = function(){
	var object = this;
	//var data = object.filters();
	var button = $('.btn-search-save',object.box);
	var text = button.find('> span');
	var form = button.find('> div');
	var input = form.find('input');

	text.addClass('hidden');
	form.removeClass('hidden');
	input.focus();
	input.off('keydown').on('keydown', function(e){
		if(e.key=="Enter") $('.btn-search-save-submit', object.box).trigger('click');
	});
};

//Passage d'une recherche en global / privé
FilterBox.prototype.core_filter_global_save = function(element){
	var object = this;
	var uid = element.attr('data-uid');
	var state = element.attr('data-global') == '1';

	state = state ? 0:1 ;

	$.action({
		action: 'core_filter_global_save',
		slug: object.slug,
		uid: uid,
		state: state,
		rightSection: object.data.rightSection
	},function(response){
		element.attr('data-global',state);
	});
};

//Enregistrement d'une recherche
FilterBox.prototype.core_filter_save = function(){
	var object = this;
	var data = object.filters();
	if(!data.keyword.length && !data.advanced.length) {
		$.message('warning', "Vous ne pouvez pas enregistrer une recherche vide");
		return;
	}

	var button = $('.btn-search-save',object.box);
	var text = button.find('> span');
	var form = button.find('> div');
	var input = form.find('input');
	text.removeClass('hidden');
	form.addClass('hidden');
	var label = input.val();
	input.val('');

	$.action({
		action : 'core_filter_save',
		slug : object.slug,
		label : label,
		filters : data
	},function(response){
		if(response.message != null)
			$.message('info',response.message);

		object.filter_search_load();
	});
};

//Rafraichissement des données et de la structure d'un filtre
FilterBox.prototype.filter_refresh = function(condition, refreshOperator, data){
	var column = $('.filter-column select',condition);
	if(data && data.column) column.val(data.column);

	var type = $('option:selected',column).attr('data-filter-type');
	var dataAttributes = this.input.find('option[value="'+column.val()+'"]').data();
	var operator = $('>.filter-operator',condition);
	var operatorSelect = $('select',operator);

	if(column.val()==""){
		 $('.filter-operator,.filter-value',condition).html('');
		return;
	}
	if(!type) return;
	//Rafraichissement OU création de l'opérateur en fonction de la colonne ciblée
	if(operator.length==0 || refreshOperator){
		var operatorTemplateNode = $('.filter-value-block[data-value-type="'+type+'"] > .filter-operator');

		if(operatorTemplateNode.length == 0){
			operatorTemplateNode = $('.filter-value-block[data-value-type="text"] > .filter-operator');
			console.warn("Le filtre de type "+type+" n'est pas défini, remplacé par un filtre text");
		}
		var operatorSelect = $(operatorTemplateNode.get(0).outerHTML);

		//On supprime les opérateurs spécifiés comme data-operator-delete si présents
		if(dataAttributes.operatorDelete){
			for(var i in dataAttributes.operatorDelete)
				operatorSelect.find('option[value="'+dataAttributes.operatorDelete[i]+'"]').remove();
		}

		//On normalise en lowercase les opérateurs
		$('option', operatorSelect).val(function(){
			return this.value.toLowerCase();
		});
		condition.find('> .filter-operator').html(operatorSelect);
	}
	if(data && data.operator) operatorSelect.val(data.operator);
	if(data && data.join) $('>.filter-join',condition).val(data.join);

	//Gestion des ous-colonnes (subcolumns)
	var subHtml = $('.filter-value-block[data-value-type="'+type+'"] .filter-subfield').html();
	condition.find('.filter-subcolumn').html(subHtml ? subHtml : '');
	if(data && data.subcolumn){
		for(var k in data.subcolumn)
			condition.find('.filter-subcolumn [data-id="'+k+'"]').val(data.subcolumn[k]);
	}
	condition.find('.filter-value').html('');

	//Types d'affichage possibles pour l'opérateur ciblé
	var optionSelect = operatorSelect.find('option:selected');
	var defaultView = optionSelect.attr('data-default-view');
	defaultView = JSON.parse(atob_unicode(defaultView));

	//On prend par defaut l'affichage n. 0
	var valueType = defaultView;

	//Si un affichage custom est spécifié, on le mets en place
	if(dataAttributes.operatorView && dataAttributes.operatorView[optionSelect.attr('value')]){
		valueType = dataAttributes.operatorView[optionSelect.attr('value')];
		//if(allowedViews.indexOf(valueType) === -1) console.warn("l'affichage "+valueType+" n'est pas conçu pour fonctionner avec l'opérateur "+optionSelect.attr('value'));
	}

	var typeBloc = $('.available-field-type[data-field-type="'+valueType.view+'"]');
	if(typeBloc.length==0){
		//Si une valeur doit être affichée (pas le cas pour "est défini" ...) mais que le type de cet affichage n'est pas en filtre, on warn
		//if(defaultView.length!=0 && allowedViews[0]!='') console.warn('Le type '+type+' est appellé dans un filtre mais n\'est pas disponible en tant que filtre');
		return;
	}
	var value = typeBloc.html();
	value = value.replace('data-template','data-type');
	var repeat = $('option:selected',operator).attr('data-values');
	repeat = repeat==null || repeat == '' ? 1 : repeat;

	for(i=0;i<repeat;i++){
		var tpl = Mustache.render(value,dataAttributes);
		tpl = tpl.replace(/\[\[/ig,'{{').replace(/\]\]/ig,'}}');
		var valueInput = $(tpl);
		condition.find('span.filter-value').append(valueInput);

		//Si le type de champ est une liste on la remplit avec le datasource
		if(dataAttributes.filterSource || dataAttributes.values){
			var source = dataAttributes.filterSource ? dataAttributes.filterSource : dataAttributes.values;
			var options = '<option value=""> - </option>';
			for(var k in source)
				options += '<option value="'+k+'">'+source[k]+'</option>';
			valueInput.append(options);
		}
		if(dataAttributes.data) valueInput.attr('data-data',JSON.stringify(dataAttributes.data));
		if(dataAttributes.values) valueInput.attr('data-values',JSON.stringify(dataAttributes.values));

		//Si on n'a qu'un champ à fill ET qu'on a plusieurs valeurs disponibles ET qu'on a un value-separator défini, on retravaille le champ des valeurs
		//Besoin de faire ça car pour les filtres qui ont plusieurs valeurs dans 1 seul champ, on split leur valeurs lors de la recherche, il faut donc les join quand on les remet
		if(repeat==1 && (data && data.value && data.value.length>1) && defaultView['value-separator']){
			values = data.value.join(defaultView['value-separator']);
			data.value = [];
			data.value[i] = values;
		}
		if(data && data.value!=null) valueInput.val(data.value[i]).attr('data-value',data.value[i]).trigger('change');
	}
	init_components(condition);
}

//Lance la recherche (via le bouton)
FilterBox.prototype.search = function(){
	var object = this;
	var filters = object.filters();

	if(filters.advanced.length>0 || filters.keyword!=''){
		filters.k = filters.keyword;
		filters.a = filters.advanced;

		delete filters.advanced;
		delete filters.keyword;

		filters.a = object.filter_rename_keys(filters.a,{join:'j',operator:'o',value:'v',type:'t',column:'c',subcolumn:'s',group:'g'});
		filters = JSON.stringify(filters);

		if(!this.data.hasOwnProperty('urlsearch') || this.data.urlsearch==true)
			$.urlParam('filters',btoa_unicode(filters));
	}else{
		$.urlParam('filters','');
	}
	window[object.data.function]();
	if(object.data.callback) window[object.data.callback]();
}

//Renommage des clés des filtres (permet de compresser la chaine base64 encodée en url)
FilterBox.prototype.filter_rename_keys = function(filters,mapping){
	object = this;
	var newFilters = [];
	for(var k in filters){
		newFilters[k] = {};
		var keys = Object.keys(filters[k]);
		for(var i in keys){
			var key = keys[i];
			if(mapping[key] === null) continue;
			newFilters[k][mapping[key]] = filters[k][key];
		}
		if(newFilters[k].g) newFilters[k].g = object.filter_rename_keys(newFilters[k].g,mapping);
		if(newFilters[k].group) newFilters[k].group = object.filter_rename_keys(newFilters[k].group,mapping);
	}
	return newFilters;
}

//Définition ou récuperation d'un tableau de filtres
FilterBox.prototype.filters = function(values){
	var object = this;

	if(values){
		object.filter_recursive_set($('.criterias', object.box),values);
		return;
	}
	filters = object.filter_recursive_get($('.criterias', object.box));

	return {
		keyword : $('.filter-keyword',object.box).val(),
		advanced : filters
	};
}

//Récupération des datas de filtre d'une ligne de filtre
FilterBox.prototype.filter_get_line = function(element){
	var object = this;
	var line = {};

	line.type = element.find('> .filter-column select option:selected').attr('data-filter-type');
	var typeData = $('.filter-value-block[data-value-type="'+line.type+'"]').data();
	line.column = element.find('> .filter-column select').val();

	if(element.find('> .filter-subcolumn [data-id]').length){
		line.subcolumn = {};
		element.find('> .filter-subcolumn [data-id]').each(function(){
			var input = $(this);
			line.subcolumn[input.attr('data-id')] = input.val();
		});
	}

	if(line.column == null || line.column == ''){
		object.core_filter_delete(element);
		return;
	} else {
		line.operator = element.find('> .filter-operator select').val();
		line.value = [];

		if(typeData.valueSelector){
			var valueElements = element.find('> .filter-value '+typeData.valueSelector);
		//Récuperation des valeurs brute sur les composants
		}else if(element.find('> .filter-value .component-raw-value').length!=0){
			var valueElements = element.find('> .filter-value .component-raw-value');
		}else{
			var valueElements = element.find('> .filter-value > .filter-value');
		}

		var dataAttributes = object.input.find('option[value="'+line.column+'"]').data();
		var options = [];
		var optionSelect = element.find('.filter-operator option:selected');
		//Types d'affichage possibles pour l'opérateur ciblé
		var defaultView = optionSelect.attr('data-default-view');
		options = JSON.parse(atob_unicode(defaultView));
		//Récupère les options de filtres qui peuvent alterer la valeur comme value-separator
		if(dataAttributes.operatorView && dataAttributes.operatorView[line.operator]){
			valueType = element.find('.filter-value .component-raw-value').attr('data-type');
			if(valueType == dataAttributes.operatorView[line.operator].view){
				if(dataAttributes.operatorView[line.operator].options) options = dataAttributes.operatorView[line.operator].options;
			}
		}
		valueElements.each(function(u,input){
			if(options['value-separator']){
				var values = $(input).val().split(options['value-separator']);
				for(var k in values)
					line.value.push(values[k]);
			}else{
				var value = $(input).val();
				if($(input).attr('type')=='checkbox') value = $(input).prop('checked') ?1:0;
				line.value.push(value);
			}
		});
		if(!element.is(':last-child')) line.join = element.find('> .filter-join').val();
	}
	return line;
}

//Définition des filtres dpeuis un objet values de manière récursive
FilterBox.prototype.filter_recursive_set = function(parent,values){
	var object = this;
	var filters = [];

	for(var key in values){
		condition = object.filter_add(null, values[key], parent);

		if(values[key].group){
			var newGroup = $('<ul class="group"></ul>');
			condition.find('.filter-column').after(newGroup);
			condition.find('.filter-column').remove();
			condition.find('.filter-operator').remove();
			condition.find('.filter-value').remove();
			if(values[key].join && values[key].join!='') condition.find('>.filter-join').val(values[key].join);
			condition.prepend(newGroup);
			object.filter_recursive_set(newGroup, values[key].group);
		}
	}
	return filters;
}

//Récuperation des filtres définis sur l'UI dans un objet et de manière récursive
FilterBox.prototype.filter_recursive_get = function(parent){
	var object = this;
	var filters = [];

	$(parent).find('> .condition:not(.hidden)').each(function(i,element){
		var filter = {};
		var element = $(element);

		if(element.find('> .group').length > 0){
			//.advanced-search-box li.condition:last-child > .filter-join
			if(!element.is(':last-child')) filter.join = element.find('> .filter-join').val();
			filter.group = object.filter_recursive_get(element.find('> .group'));
		} else {
			filter = object.filter_get_line(element);
			if(!filter) return;
		}
		if(!filter.group || filter.group.length) filters.push(filter);
	});
	return filters;
}

//Ajout d'un filtre visuel (vide ou remplis avec l'obj data) dans un parent (optionnel) ou après un element (optionnel)
//si pas de parent ou d'élements défini, le filtre s'ajoute au dernier niveau.
FilterBox.prototype.filter_add = function(element, data, parent){
	var object = this;
	if(!data) data = {};
	data.columns = object.columns;

	var condition = $(Mustache.render(object.tpl, data));
	condition.removeClass('hidden');

	if(element){
		element.after(condition);
	}else if(parent){
		parent.append(condition);
	}else{
		$('.criterias',object.box).append(condition);
	}
	if(data) object.filter_refresh(condition, true, data);

	$(condition.parent()).sortable({
		axis : 'y',
		handle: ".btn-move",
	});
	return condition;
}

//Duplication d'un filtre visuel (ligne ou groupe) dans un parent (optionnel) ou après un element (optionnel)
//si pas de parent ou d'élement défini, le filtre s'ajoute au dernier niveau.
FilterBox.prototype.filter_duplicate = function(element, parent){
	var object = this;

	//Tweak JS car jQuery ne capte pas bien les selected sur les clones
	element.find(':selected').attr('selected','selected');

	//On clone la ligne ou le groupe
	var condition = element.clone(true).removeClass('hover');
	//On la wrappe dans un wrapper temporaire pour récupérer les datas
	var wrapper = $('<div class="temp-filter-wrapper"></div>');
	wrapper.append(condition);

	//On récupère les datas
	filters = object.filter_recursive_get(wrapper);

	//On set à nouveau les datas
	object.filter_recursive_set(element.parent(), filters);

	//On supprime les attributs selected
	$('.criterias', object.box).find(':selected').removeAttr('selected');
}

//Execution d'une recherche enregistrées
FilterBox.prototype.filter_search_execute = function(uid,showFilters,callback){
	var object = this;
	var search = object.searches[uid];
	$.urlParam('filters','');
	$('.condition:not(.hidden)',object.box).each(function(i,element){
		object.core_filter_delete($(element));
	});
	$('.condition',object.box).removeClass('error');
	$('.filter-keyword',object.box).val(search.filters.keyword);

	if(showFilters) object.box.addClass('advanced');
	object.filter_recursive_set(null,search.filters.advanced);
	object.search();

	if (callback) callback();
	//object.load_callback();
}

//Recherche des recherches enregistrées
FilterBox.prototype.filter_search_load = function(callback){
	var object = this;

	$.action({
		action : 'core_filter_search',
		slug : object.slug,
	},function(response){
		var savedSearch = $('.saved-search-container');
		var tpl = $('template.filter-saved-search',object.box).html();

		$('.btn-search-load',object.box).remove();
		response.filters.length==0 ? savedSearch.addClass('hidden') : savedSearch.removeClass('hidden');

		var rows = [];
		for(var i in response.filters){
			var search = response.filters[i];
			if(!search.label) continue;

			var line = Mustache.render(tpl,search);
			savedSearch.append(line);
			rows.push(search);
		}

		object.searches = response.filters;

		if(object.box.attr('data-user-shortcut')!='' || object.box.attr('data-global-shortcut')!=''){
			var userShortcuts = object.box.attr('data-user-shortcut') != ''
				? $(object.box.attr('data-user-shortcut'))
				: null;

			var globalShortcuts = object.box.attr('data-global-shortcut') != ''
				? $(object.box.attr('data-global-shortcut'))
				: null;

			if(rows.length!=0 && rows.filter(row => row.global == 0) && userShortcuts) {
				userShortcuts.find('.has-shortcut').removeClass('hidden');
			} else {
				userShortcuts.find('.has-shortcut').addClass('hidden');
			}

			if(rows.length!=0 && rows.filter(row => row.global == 1) && globalShortcuts) {
				globalShortcuts.find('.has-shortcut').removeClass('hidden');
			} else {
				globalShortcuts.find('.has-shortcut').addClass('hidden');
			}

			//Ajout des recherches enregistrées sur les zones de raccourcis (globaux et user)
			if(userShortcuts.is(globalShortcuts)) {
				userShortcuts.find('>ul').addLine(rows);
			} else {
				if(userShortcuts) userShortcuts.find('>ul').addLine(rows.filter(row => row.global == 0));
				if(globalShortcuts) globalShortcuts.find('>ul').addLine(rows.filter(row => row.global == 1));
			}

			if(object.slug && $.urlParam('filters') && object.box.attr('data-select-from-url')=="true"){
		 		var uid = object.filter_select();
		 		if(uid){
		 			userShortcuts.find('div[uid="'+uid+'"]').addClass('active');
		 			globalShortcuts.find('div[uid="'+uid+'"]').addClass('active');
		 		}
	 		}
		}
		if(callback) callback();
	});
};

//Supression d'un filtre et de ses parents vides.
FilterBox.prototype.core_filter_delete = function(line){
	var object = this;
	var group = line.closest('ul.group');

	//Suppression des groupes avec 1 seule ligne
	if(line.closest('ul.group').find('>li.condition:not(.hidden)').length==1){
		object.filter_add(line.find('ul.group')?line:null);
		line.remove();
		return;
	}
	if($('.criterias > .condition:not(.hidden)',object.box).length>1 || line.siblings('.condition:not(.hidden)').length>0) line.remove();

	//Suppression ascendante récursive des groupes vides après suppression de la ligne
	while(1){
		if(!group || group.length == 0 || group.find('.condition').length!=0) break;
		oldgroup = group.parent().parent();
		group.parent().remove();
		group = oldgroup;
	}
}

//Selection d'un filtre raccourci si equivalent au filtre de l'URL
FilterBox.prototype.filter_select = function(){
	var object = this;

	var urlFilters = $.urlParam('filters');
	urlFilters = JSON.parse(atob_unicode(urlFilters));
	if(urlFilters && urlFilters!=''){
		urlFilters.keyword = urlFilters.k;
		urlFilters.advanced = urlFilters.a;
		delete urlFilters.a;
		delete urlFilters.k;

		urlFilters.advanced = object.filter_rename_keys(urlFilters.advanced,{j:'join',o:'operator',v:'value',t:'type',c:'column',s:'subcolumn',g:'group'});
 	}
	var slug = false;
	Object.keys(object.searches).forEach(function(key){
		var search = object.searches[key]['filters'];
		if(JSON.stringify(search) == JSON.stringify(urlFilters)) slug = key;
	});
	return slug;
}