<cfsilent><!--- утилита для передачи стека вызовов через URL --->
<!---v1.01 14:00 03.04.2019 added output param thisURL--->
<!---v1.02 06:16 26.12.2019 symbols -._ are not encoded --->

<cfparam name="ATTRIBUTES.thisUrl" type="string" default=""><!--- текущий URL без трэка --->
<cfparam name="ATTRIBUTES.output" type="string" default="tr">
<cfparam name="ATTRIBUTES.input" type="string" default="track"><!--- имя параметра в URL или FORM, в которой лежит текущий трэк --->
<cfparam name="ATTRIBUTES.urlParamName" type="string" default="#ATTRIBUTES.input#">
<cfparam name="ATTRIBUTES.defaultBackUrl" type="string">

<!---<cffunction name="urlSafeWrap">
	<cfargument name="s" type="string"/>
	<cfreturn replace(URLEncodedFormat(s), "%", "!", "ALL")/>
</cffunction>--->
<!---<cffunction name="urlSafeWrap">
	<cfargument name="s" type="string"/>
	<cfreturn 
		replace(
			replaceList(
				URLEncodedFormat(s),
				"#URLEncodedFormat('_')#,#URLEncodedFormat('.')#,#URLEncodedFormat('-')#,#URLEncodedFormat('~')#,#URLEncodedFormat('=')#",
				"_,.,-,~,="
			),
			"%", "!", "ALL"
		)/>
</cffunction>--->

<cffunction name="urlSafeWrap">
	<cfargument name="s" type="string"/>
	<cfreturn 
		replace(
			replaceList(
				URLEncodedFormat(s),
				"#URLEncodedFormat('_')#,#URLEncodedFormat('.')#,#URLEncodedFormat('-')#",
				"_,.,-"
			),
			"%", "!", "ALL"
		)/><!---unreserved  = ALPHA / DIGIT / "-" / "." / "_" / "~" https://www.ietf.org/rfc/rfc3986.txt--->
</cffunction>

<cffunction name="urlSafeUnWrap">
	<cfargument name="s" type="string"/>
	<cfreturn URLDecode(replace(s, "!", "%","ALL"))/>
</cffunction>


<cfif StructKeyExists(form, ATTRIBUTES.input)>
	<cfset track=structFind(form, ATTRIBUTES.input)/>
<cfelseif StructKeyExists(url, ATTRIBUTES.input)>
	<cfset track=structFind(url, ATTRIBUTES.input)/>
<cfelse>
	<cfset track=""/>
</cfif>	

<!--- для предохранения спецсимволов (особенно запятых) в урле используется заворачивание в URLEncodedFormat. ColdFusion (по крайней мере старые версии) не умеет возвращать множественные значения иначе, как списком через запятую. Чтобы избежать непрошенного разворачивания URLEncodedFormat при отправке GETом, мы заменяем % на !. 
Реализовал вариант с заворачиванием в GZIP, работает неплохо, но вариант с URLEncodedFormat сохраняет хоть какую-то читабельность.
Можно было бы кодировать в base64. Или вообще хранить на сервере в сессии, а передавать хэш - но непонятно, когда можно забыть этот урл, а накапливать их зря в сессии не хочется. 
Попробовал Base64 - очень плохо, все размножается при повторном кодировании и начинает виснуть. 
--->

<cfset trackBack="">

<cfif listLen(track) GT 0>
	<cfset trackBack=ListDeleteAt(track, listLen(track))>
</cfif>

<cfset trackForward=ListAppend(track, urlSafeWrap(ATTRIBUTES.thisUrl))/>
<cfset backUrlBase=urlSafeUnWrap(listLast(track))/>

<cfif len(backUrlBase) GT 0>	
	<cfif len(trackBack) GT 0>
		<cfif find("?", backUrlBase) GT 0>
			<!--- has query string --->
			<cfset backUrl="#backUrlBase#&#ATTRIBUTES.urlParamName#=#trackBack#"/>
		<cfelse>
			<!--- no query string --->
			<cfset backUrl="#backUrlBase#?#ATTRIBUTES.urlParamName#=#trackBack#"/>
		</cfif>
	<cfelse>
		<cfset backUrl="#backUrlBase#"/>
	</cfif>	
<cfelse>	
	<cfset backUrl="#ATTRIBUTES.defaultBackUrl#"/>
</cfif>

<cfif NOT (find("?", ATTRIBUTES.thisUrl) GT 0)>
	<cfset selfUrl="#ATTRIBUTES.thisUrl#?#ATTRIBUTES.urlParamName#=#track#"/>
<cfelse>
	<cfset selfUrl="#ATTRIBUTES.thisUrl#&#ATTRIBUTES.urlParamName#=#track#"/>
</cfif>

<cfset setVariable("CALLER.#ATTRIBUTES.output#", structNew())/>
<cfset setVariable("CALLER.#ATTRIBUTES.output#.fw", trackForward)/><!--- значение стэка: &track=#tr.fw# --->
<cfset setVariable("CALLER.#ATTRIBUTES.output#.fwx", "#ATTRIBUTES.urlParamName#=#trackForward#")/><!--- готовый суффикс для URL: &#tr.fwx#--->
<cfset setVariable("CALLER.#ATTRIBUTES.output#.bk", trackBack)/>
<cfset setVariable("CALLER.#ATTRIBUTES.output#.backUrl", backUrl)/>
<cfset setVariable("CALLER.#ATTRIBUTES.output#.self", track)/>
<cfset setVariable("CALLER.#ATTRIBUTES.output#.selfUrl", selfUrl)/>
<cfset setVariable("CALLER.#ATTRIBUTES.output#.thisUrl", ATTRIBUTES.thisUrl)/>
<cfset setVariable("CALLER.#ATTRIBUTES.output#.len", listLen(track))/>

<cfexit method="exitTag"/>
</cfsilent>
