Slider Formtool

Sean Coyne has created a slider formtool and thought it could be useful to others in the community. You can find the source for it here: https://gist.github.com/1230366

To use, simply download the CFC, and place it in your projects /farcry/projects/projectname/packages/formtools directory. Once there, you specify the metadata in your custom types as you would use any other formtool.

<cfproperty ftLabel="Num. of Apples" name="numApples" type="integer" ftType="slider" ftMin="1" ftMax="100" ftStep="1" ftOrientation="horiztonal" />

The formtool is an extension of the numeric formtool found in core, so you can use any of the features found from that formtool in the slider (ftPrefix, ftSuffix, etc).

The slider formtool adds 4 new metadata options. ftMin, ftMax, ftStep, and ftOrientation. Min and Max are the lowest and highest values allowed for the field. ftStep is the increment factor the slider will use. If you specify one, each slide of the slider will move the value by 1, if you specify 0.5, it will increment it by 0.5 (1, 1.5, 2, etc). ftOrientation can be either "horizontal" or "vertical" and it will orient the slider either horizontally or vertically.

Your users can either type a value into the text box or use the slider to select a value.

This uses the jQuery UI Slider widget (http://jqueryui.com/demos/slider/) so you can use it with FarCry 6+ since core ships with jQuery UI built in.

If you find it useful, missing a feature, broken, etc please let me know. Consider it released under a "do whatever you want with it" license.

./yourproject/packages/formtools/slider.cfc
<cfcomponent extends="farcry.core.packages.formtools.numeric" name="slider" displayname="slider" hint="Field component to display a slider">

	<cfproperty name="ftMin" required="false" type="numeric" default="0" hint="The minimum value of the slider." />
	<cfproperty name="ftMax" required="false" type="numeric" default="10" hint="The maximum value of the slider." />
	<cfproperty name="ftStep" required="false" type="numeric" default="1" hint="Determines the size or amount of each interval or step the slider takes between min and max. The full specified value range of the slider (max - min) needs to be evenly divisible by the step." />
	<cfproperty name="ftOrientation" required="false" type="string" default="horizontal" options="horizontal,vertical" hint="This option determines whether the slider has the min at the left, the max at the right or the min at the bottom, the max at the top. Possible values: 'horizontal', 'vertical'." />
	
	<cffunction name="edit" access="public" output="true" returntype="string" hint="his will return a string of formatted HTML text to enable the user to edit the data">
		<cfargument name="typename" required="true" type="string" hint="The name of the type that this field is part of.">
		<cfargument name="stObject" required="true" type="struct" hint="The object of the record that this field is part of.">
		<cfargument name="stMetadata" required="true" type="struct" hint="This is the metadata that is either setup as part of the type.cfc or overridden when calling ft:object by using the stMetadata argument.">
		<cfargument name="fieldname" required="true" type="string" hint="This is the name that will be used for the form field. It includes the prefix that will be used by ft:processform.">
		
		<cfset var html = "" />
		
		<cfparam name="arguments.stMetadata.ftMin" default="0" />
		<cfparam name="arguments.stMetadata.ftMax" default="10" />
		<cfparam name="arguments.stMetadata.ftStep" default="1" />
		<cfparam name="arguments.stMetadata.ftOrientation" default="horizontal" />
		
		<!--- metadata inherited from numeric formtool --->
		<cfparam name="arguments.stMetadata.ftIncludeDecimal" default="false">
		<cfparam name="arguments.stMetadata.ftCurrencySymbol" default="">
		<cfparam name="arguments.stMetadata.ftPrefix" default="">
		<cfparam name="arguments.stMetadata.ftSuffix" default="">
		<cfparam name="arguments.stMetadata.ftMask" default="">
		
		<!--- format the value (same as numeric formtool) --->
		<cfif len(arguments.stMetadata.ftMask)>
			<cfset arguments.stMetadata.value = trim(NumberFormat(arguments.stMetadata.value, arguments.stMetadata.ftMask))>
		<cfelse>
			<!--- This is for legacy. You should use just ftPrefix and ftSuffix --->
			<cfif len(arguments.stMetadata.ftCurrencySymbol)>
				<cfset arguments.stMetadata.ftPrefix = arguments.stMetadata.ftCurrencySymbol />
			</cfif>

			<cfif stMetadata.ftIncludeDecimal>
				<cfset arguments.stMetadata.value = DecimalFormat(arguments.stMetadata.value)>
			<cfelse>
				<cfset arguments.stMetadata.value = NumberFormat(arguments.stMetadata.value)>
			</cfif>
		</cfif>
		
		<cfimport taglib="/farcry/core/tags/webskin" prefix="skin" />
		
		<skin:loadJs id="jquery" />
		<skin:loadJs id="jquery-ui" />
		<skin:loadCss id="jquery-ui" />
		
		<skin:onReady>
			<cfoutput>
			jQuery("###arguments.fieldname#-slider").slider({
				min: #arguments.stMetadata.ftMin#,
				max: #arguments.stMetadata.ftMax#,
				step: #arguments.stMetadata.ftStep#,
				value: #reReplace(arguments.stMetadata.value, "[^0-9.\-]", "", "ALL")#,
				orientation: '#arguments.stMetadata.ftOrientation#',
				slide: function (event, ui) {
					jQuery("###arguments.fieldname#").val('#jsStringFormat(arguments.stMetadata.ftPrefix)#' + ui.value + '#jsStringFormat(arguments.stMetadata.ftSuffix)#');
				} 
			});
			jQuery("###arguments.fieldname#").keyup(function(event){
				var thisVal = jQuery(this).val().replace(/[^0-9.\-]/g,'');
				var thisFormattedVal = '#jsStringFormat(arguments.stMetadata.ftPrefix)#' + thisVal + '#jsStringFormat(arguments.stMetadata.ftSuffix)#';
				var maxFormattedVal = '#jsStringFormat(arguments.stMetadata.ftPrefix)#' + #arguments.stMetadata.ftMax# + '#jsStringFormat(arguments.stMetadata.ftSuffix)#';
				var minFormattedVal = '#jsStringFormat(arguments.stMetadata.ftPrefix)#' + #arguments.stMetadata.ftMin# + '#jsStringFormat(arguments.stMetadata.ftSuffix)#';
				
				jQuery(this).val(thisFormattedVal);
				
				if (thisVal >= #arguments.stMetadata.ftMin# && thisVal <= #arguments.stMetadata.ftMax#) {
					
					// good value, set the slider value
					jQuery("###arguments.fieldname#-slider").slider("value", thisVal);
					
				} else if (thisVal > #arguments.stMetadata.ftMax#) {
				
					// too high, set it to the maximum value
					jQuery("###arguments.fieldname#-slider").slider("value", thisVal);
					jQuery(this).val(maxFormattedVal);
					
				} else if (thisVal < #arguments.stMetadata.ftMin#) {
					
					// too low, set it to the minimum value
					jQuery("###arguments.fieldname#-slider").slider("value", thisVal);
					jQuery(this).val(minFormattedVal);
					
				}
			});
			</cfoutput>
		</skin:onReady>
		
		<cfsavecontent variable="html">
			<cfoutput>
				<div class="multiField">
					<div class="sliderInput" style="margin-bottom: 10px;">
						<div id="#arguments.fieldname#-slider"></div>
					</div>
					#super.edit(argumentCollection = arguments)#
				</div>
			</cfoutput>
		</cfsavecontent>
		
		<cfreturn html />
	</cffunction>
	
	<cffunction name="validate" access="public" output="true" returntype="struct" hint="This will return a struct with bSuccess and stError">
		<cfargument name="objectid" required="true" type="string" hint="The objectid of the object that this field is part of.">
		<cfargument name="typename" required="true" type="string" hint="The name of the type that this field is part of.">
		<cfargument name="stFieldPost" required="true" type="struct" hint="The fields that are relevent to this field type.">
		<cfargument name="stMetadata" required="true" type="struct" hint="This is the metadata that is either setup as part of the type.cfc or overridden when calling ft:object by using the stMetadata argument.">
		
		<cfset var stResult = super.validate(argumentCollection = arguments) />
		
		<cfparam name="arguments.stMetadata.ftMin" default="0" />
		<cfparam name="arguments.stMetadata.ftMax" default="10" />
		
		<!--- if its required, make sure we have a value --->
		
		<cfif structKeyExists(arguments.stMetadata, "ftValidation") AND listFindNoCase(arguments.stMetadata.ftValidation, "required") AND NOT len(trim(stFieldPost.Value))>
			<cfset stResult = failed(value = stResult.value, message = "This is a required field.") />
		</cfif>
		
		<!--- ensure value is within range --->
		
		<cfif stResult.value lt arguments.stMetadata.ftMin>
			<cfset stResult = failed(value = stResult.value, message = "Value must be greater than #arguments.stMetadata.ftMin#") />
		</cfif>
		
		<cfif stResult.value gt arguments.stMetadata.ftMax>
			<cfset stResult = failed(value = stResult.value, message = "Value must be less than #arguments.stMetadata.ftMax#") />
		</cfif>
		
		<cfreturn stResult />
		
	</cffunction>
	
</cfcomponent>