<!--- version 2.01---><!---15:25 31.01.2019--->
<!--- 2017.06.16 исправлен баг с очисткой при числовом значении по умолчанию --->
<!---v0.5 10:02 02.01.2019--->
<cfassociate basetag="cf_filter_settings"/>
<cfparam name="ATTRIBUTES.filter" type="struct" default=#getBaseTagData("CF_FILTER_SETTINGS",1).filter#/><!--- *** possible Railo bug, getBaseTagData works only with second parameter --->
<cfparam name="ATTRIBUTES.param" type="string"/>
<cfparam name="ATTRIBUTES.default" type="string" default=""/><!--- список: значения, установленные по умолчанию. Обычно состоит из 1 значения даже для списка. В случае кодирования base64 этот список НЕ кодируется (проблемы с запятыми остаются на совести разработчика)--->
<!---*** И вообще Base64 нельзя использовать непосредственно, он не URLsafe --->
<cfparam name="ATTRIBUTES.ftype" type="string" default="string"/>
<cfparam name="ATTRIBUTES.list" type="boolean" default="No"/>
<cfparam name="ATTRIBUTES.compare" type="string" default="EQ"/>
<cfparam name="ATTRIBUTES.field" type="string" default=""/>
<cfparam name="ATTRIBUTES.base64" type="boolean" default="No"/><!--- означает, что параметр нужно раскодировать из base64---><!---*** Переделать на URL-safe base64--->

<!---
CFQUERYPARAM  CFSQLTYPE="CF_SQL_VARCHAR" ставит апострофы вокруг значения параметра. 
Поэтому приходится любые wildcards засовывать в значение bind variable. Запрос должен иметь вид
SELECT A,B FROM C WHERE A LIKE ? 
(без всяких апострофов), а значение параметра будет, например, 
"%#submitted_string#%"
--->
<cfparam name="ATTRIBUTES.prefix" type="string" default=""/>
<cfparam name="ATTRIBUTES.suffix" type="string" default=""/>
<cfparam name="ATTRIBUTES.expression" type="string" default=""/>

<!---
Хранение данных для списков
переделано обратно с массива на лист. 
При этом случилась забавная, но неприятная и трудноуловимая ошибка
arrayAppend(array,item) механически заменил на listAppend(list,item)
а надо было на list=listAppend(list,item) 
--->
 
<!---<cfif ATTRIBUTES.list>
	<cfset def = "">
	<cfloop list=#ATTRIBUTES.default# index="item">
		<cfset listAppend(def, item)>Список значений по умолчанию НЕ должен быть кодирован в base64 при ATTRIBUTES.base64
	</cfloop>
<cfelse>
	
</cfif>--->
<cfset def=ATTRIBUTES.default/>
<!---<cfoutput>#ATTRIBUTES.param#:</cfoutput>--->

<!---<cfif evaluate("isDefined('CALLER.#ATTRIBUTES.param#')")>
	<cfset evaluate("val=(CALLER.#ATTRIBUTES.param#)")>--->
	
<cfif structKeyExists(CALLER, '#ATTRIBUTES.param#')>
	<cfset val=structFind(CALLER, '#ATTRIBUTES.param#')/>
	<!--- parse --->
	
	<cfset to=1/>
	<cfset values = ""/>
	
	<cfif ATTRIBUTES.list>
		<cfset to=listLen(val)/>

	</cfif>
	
	<cfloop from="1" to=#to# index="i">		
		
		<cfif ATTRIBUTES.list>
			<cfset element=listGetAt(val,i)/>		
		<cfelse>
			<cfset element=val/>
		</cfif>
		
		<cfswitch expression=#ATTRIBUTES.ftype#>	
		
			<cfcase value="numeric">
				<cfset element=trim(element)>
				<cfif NOT isNumeric(element)>
					<cfset element=def/>
				</cfif>
			</cfcase>
			
			<cfcase value="guid">
				<cfset element=trim(element)>
				<cfif NOT isValid('guid',element)>
					<cfset element=def/>
				</cfif>
			</cfcase>
			
			<cfcase value="date">
				<cftry>
					<cfset element=CreateDateTime(ListGetAt(element,3,'.'), ListGetAt(element,2,'.'), ListGetAt(element,1,'.'),0,0,0)>
				<cfcatch type="Any">
					<!--- <cfoutput>#cfcatch.message# #cfcatch.detail#</cfoutput> --->
				</cfcatch>
				</cftry>
			</cfcase>
			
			<cfcase value="string">
				<cfif ATTRIBUTES.base64>
					<cfset element=ToString(BinaryDecode(element , "base64"))>
				</cfif>
				<cfset element=safeStr(element)/>
			</cfcase>
						
			<cfdefaultcase>
				<cfthrow message="Filterparam: unsupported ftype value '#ATTRIBUTES.ftype#'" detail="Supported values are 'numeric', 'date', 'string'">
			</cfdefaultcase>
			
		</cfswitch>
		
		<cfif ATTRIBUTES.list>
			<cfset values=listAppend(values, element)/>
		<cfelse>
			<cfset val=element/>
			<cfbreak/>
		</cfif>
		
	</cfloop>	
	
	<cfif ATTRIBUTES.list>
		<cfset val=values/><!---*** все это выглядит ужасно. Переиспользование переменных вперед-назад, IF вокруг листа--->
	</cfif>

	<!---/parse --->

<cfelseif structKeyExists(ATTRIBUTES.filter, ATTRIBUTES.param)><!--- если параметр есть в сохраненном фильтре, взять оттуда --->

	<cfset filterItem = structFind(ATTRIBUTES.filter, ATTRIBUTES.param)>
	<cfif isStruct(filterItem)>
		<cfset val=filterItem.val>
	<cfelse><!---not struct <cfoutput>#ATTRIBUTES.param#</cfoutput>--->
		<cfset val=def>
	</cfif>	
	
<cfelse>

	<cfset val=def>
	
</cfif>


<cfset setVariable("CALLER.#ATTRIBUTES.param#", "")/>
<cftry>
	<cfset setVariable("CALLER.#ATTRIBUTES.param#",#val#)/>
	<cfcatch type="Any"></cfcatch>
</cftry>


<cfif shouldRemoveValue(val, def)>
	<cfset structDelete(ATTRIBUTES.filter, ATTRIBUTES.param)/>
<cfelse>
	<cfset fltItem=structNew()> 
	<cfset fltItem.ftype=ATTRIBUTES.ftype>
	<cfset fltItem.list=ATTRIBUTES.list>
	<cfset fltItem.field=ATTRIBUTES.field>
	<cfset fltItem.compare=ATTRIBUTES.compare>
	<cfset fltItem.val=val>
	<cfset fltItem.prefix=ATTRIBUTES.prefix/>
	<cfset fltItem.suffix=ATTRIBUTES.suffix/>
	<cfset fltItem.expression=ATTRIBUTES.expression/>
	<cfset structInsert(ATTRIBUTES.filter,ATTRIBUTES.param,fltItem, "TRUE")>
</cfif>

<cfexit method="exittag"/>

<cffunction name="shouldRemoveValue" returnType="boolean">
	<cfargument name="val"/>
	<cfargument name="def"/>
	<!---<cfdump var=#val#>	<cfdump var=#def#>--->
	<cfscript>
		if (!IsSimpleValue(val)){
			if (isArray(val)) {
				return (arrayLen(val) == 0);
			}
			return false;
		}
		return ((val==def) OR (val==""));
	</cfscript>
</cffunction>

<cffunction name="safeStr" returnType="string">
	<cfargument name="val" type="string"/>
	<cfreturn replaceList(val, "',"",<,>", ",,,")/>
</cffunction>

