 					// if no other form parameter exists with this name it means that the checkbox was left blank and therefore we force the value to the unchecked values for the checkbox (to get around the problem that unchecked checkboxes don't post at all)
 					loc.formParamName = ReplaceNoCase(loc.key, "($checkbox)", "");
 					if (!StructKeyExists(loc.returnValue, loc.formParamName))
-						loc.returnValue[loc.formParamName] = loc.returnValue[loc.key];
+					{
+						loc.returnValue[loc.formParamName] = [];
+						loc.iEnd = ArrayLen(loc.returnValue[loc.key]);
+						for (loc.i = 1; loc.i lte loc.iEnd; loc.i++)
+							loc.returnValue[loc.formParamName][loc.i] = loc.returnValue[loc.key][loc.i];
+					}
 					StructDelete(loc.returnValue, loc.key);
 				}
 				else if (REFindNoCase(".*\((\$year|\$month|\$day|\$hour|\$minute|\$second)\)$", loc.key))

	<cfscript>
		var loc = {};

		// combine the form and url scopes into one scope.
		// url variables take precedence.
		loc.returnValue = Duplicate(arguments.formScope);
		StructDelete(loc.returnValue, "fieldnames", false);
		StructAppend(loc.returnValue, arguments.urlScope, true);

		// go through the matching route pattern and add URL variables from the route to the struct
		loc.iEnd = ListLen(arguments.foundRoute.pattern, "/");
		for (loc.i=1; loc.i <= loc.iEnd; loc.i++)
		{
			loc.item = ListGetAt(arguments.foundRoute.pattern, loc.i, "/");
			if (Left(loc.item, 1) == "[")
				loc.returnValue[ReplaceList(loc.item, "[,]", ",")] = ListGetAt(arguments.route, loc.i, "/");
		}

		// add controller and action unless they already exist
		if (!StructKeyExists(loc.returnValue, "controller"))
			loc.returnValue.controller = arguments.foundRoute.controller;
		if (!StructKeyExists(loc.returnValue, "action"))
			loc.returnValue.action = arguments.foundRoute.action;

		// convert controller to upperCamelCase and action to normal camelCase
		loc.returnValue.controller = REReplace(loc.returnValue.controller, "-([a-z])", "\u\1", "all");
		loc.returnValue.action = REReplace(loc.returnValue.action, "-([a-z])", "\u\1", "all");

		// add name of route to params if a named route is running
		if (StructKeyExists(arguments.foundRoute, "name") && Len(arguments.foundRoute.name) && !StructKeyExists(loc.returnValue, "route"))
			loc.returnValue.route = arguments.foundRoute.name;

		// decrypt all values except controller and action
		if (application.wheels.obfuscateUrls)
		{

				if (StructKeyExists(loc.returnValue, "#loc.key#($second)"))
					StructDelete(loc.returnValue, "#loc.key#($second)");
			}
			// add form variables to the params struct
			$createNestedParamStruct(params=loc.returnValue);
			// find any new structs and convert them
			$createNewArrayStruct(params=loc.returnValue);
		}
		
		/************************************
		*	We now do the routing and controller
		*	params after we have built all other params
		*	so that we don't have more logic around
		*	params in arrays
		************************************/

		// go through the matching route pattern and add URL variables from the route to the struct
		loc.iEnd = ListLen(arguments.foundRoute.pattern, "/");
		for (loc.i=1; loc.i <= loc.iEnd; loc.i++)
		{
			loc.item = ListGetAt(arguments.foundRoute.pattern, loc.i, "/");
			if (Left(loc.item, 1) == "[")
				loc.returnValue[ReplaceList(loc.item, "[,]", ",")] = ListGetAt(arguments.route, loc.i, "/");
		}

		// add controller and action unless they already exist
		if (!StructKeyExists(loc.returnValue, "controller"))
			loc.returnValue.controller = arguments.foundRoute.controller;
		if (!StructKeyExists(loc.returnValue, "action"))
			loc.returnValue.action = arguments.foundRoute.action;

		// convert controller to upperCamelCase and action to normal camelCase
		loc.returnValue.controller = REReplace(loc.returnValue.controller, "-([a-z])", "\u\1", "all");
		loc.returnValue.action = REReplace(loc.returnValue.action, "-([a-z])", "\u\1", "all");

		// add name of route to params if a named route is running
		if (StructKeyExists(arguments.foundRoute, "name") && Len(arguments.foundRoute.name) && !StructKeyExists(loc.returnValue, "route"))
			loc.returnValue.route = arguments.foundRoute.name;
	</cfscript>
	<cfreturn loc.returnValue>
</cffunction>

<cffunction name="$getParameterMap" returntype="struct" access="public" output="false">
	<cfargument name="formScope" type="struct" required="true">
	<cfargument name="urlScope" type="struct" required="true">
	<cfargument name="parameters" type="struct" required="false" default="#Duplicate(GetPageContext().getRequest().getParameterMap())#">
	<cfargument name="multipart" type="any" required="false" default="#form.getPartsArray()#">
	<cfscript>
		var loc = {};
		loc.original = {};
		loc.multipart = {};
		loc.parameters = {};
		// merge our coldfusion scopes together
		loc.original = Duplicate(arguments.formScope);
		StructDelete(loc.original, "fieldnames", false);
		StructAppend(loc.original, arguments.urlScope, true);
		// if the form our url scopes were set after the request started, we need to make sure that they are in arrays
		for (loc.item in loc.original)
		{
			loc.parameters[loc.item] = [];
			ArrayAppend(loc.parameters[loc.item], loc.original[loc.item]);
		}
		// get our values from the parameter map, this will always contain url variables and will contain form parameters when not a multipart form
		for (loc.item in arguments.parameters)
		{
			loc.parameters[loc.item] = [];
			loc.iEnd = ArrayLen(arguments.parameters[loc.item]);
			for (loc.i = 1; loc.i lte loc.iEnd; loc.i++)
				loc.parameters[loc.item][loc.i] = arguments.parameters[loc.item][loc.i];
		}
		// if this is defined, then we have a multipart for and will be getting our form values from here
		if (StructKeyExists(arguments, "multipart"))
		{
			// build our multipart struct
			loc.iEnd = ArrayLen(arguments.multipart);
			for (loc.i = 1; loc.i lte loc.iEnd; loc.i++)
			{
				loc.param = arguments.multipart[loc.i];
				if (loc.param.isParam())
				{
					if (!StructKeyExists(loc.multipart, loc.param.getName()))
						loc.multipart[loc.param.getName()] = [];
					ArrayAppend(loc.multipart[loc.param.getName()], loc.param.getStringValue());
				}
			}
			
			// now overwrite our parameters with the multipart values
			for (loc.item in loc.multipart)
				loc.parameters[loc.item] = loc.multipart[loc.item];
		}
		// overwrite any parameters in our map with the right values from the url
		for (loc.item in arguments.urlScope)
		{
			loc.parameters[loc.item] = [];
			ArrayAppend(loc.parameters[loc.item], arguments.urlScope[loc.item]);
		}
	</cfscript>
	<cfreturn loc.parameters />
</cffunction>

<cffunction name="$createNestedParamStruct" returntype="struct" access="public" output="false">
	<cfargument name="params" type="struct" required="true" />
	<cfscript>
		var loc = {};
		for (loc.key in arguments.params)
		{
			if (Find("[", loc.key) && Right(loc.key, 1) == "]")
			{
				// object form field
				loc.name = SpanExcluding(loc.key, "[");
				// we split the key into an array so the developer can have multiple levels of params passed in
				loc.nested = ListToArray(ReplaceList(loc.key, loc.name & "[,]", ""), "[", true); 
				if (!StructKeyExists(arguments.params, loc.name))
					arguments.params[loc.name] = {};
				loc.struct = arguments.params[loc.name]; // we need a reference to the struct so we can nest other structs if needed
				loc.iEnd = ArrayLen(loc.nested);
				for (loc.i = 1; loc.i lte loc.iEnd; loc.i++) // looping over the array allows for infinite nesting
				{
					loc.item = loc.nested[loc.i];
					if (!Len(loc.item)) // if we have an empty struct item it means that the developer is passing in new items
						loc.item = "new";
					if (!StructKeyExists(loc.struct, loc.item))
						loc.struct[loc.item] = {};
					if (loc.i != loc.iEnd)
						loc.struct = loc.struct[loc.item]; // pass the new reference (structs pass a reference instead of a copy) to the next iteration
					else if (IsArray(arguments.params[loc.key]) && ArrayLen(arguments.params[loc.key]) == 1)
						loc.struct[loc.item] = arguments.params[loc.key][1];
					else if (IsArray(arguments.params[loc.key]))
						loc.struct[loc.item] = $arrayToStruct(arguments.params[loc.key]);
					else
						loc.struct[loc.item] = arguments.params[loc.key];
				}
				// delete the original key so it doesn't show up in the params
				StructDelete(arguments.params, loc.key, false);
			}
			else if (IsArray(arguments.params[loc.key]) && ArrayLen(arguments.params[loc.key]) == 1)
				arguments.params[loc.key] = arguments.params[loc.key][1];
		}	
	</cfscript>
	<cfreturn arguments.params />
</cffunction>

<cffunction name="$createNewArrayStruct" returntype="void" access="public" output="false">
	<cfargument name="params" type="struct" required="true" />
	<cfscript>
		var loc = {};
		
		loc.newStructArray = StructFindKey(arguments.params, "new", "all");
		loc.iEnd = ArrayLen(loc.newStructArray);
		
		for (loc.i = 1; loc.i lte loc.iEnd; loc.i++)
		{
			loc.owner = loc.newStructArray[loc.i].owner.new;
			loc.value = loc.newStructArray[loc.i].value;
			loc.map = {};
			$mapStruct(map=loc.map, struct=loc.value);
			
			StructClear(loc.value); // clear the struct now that we have our paths and values
			
			for (loc.item in loc.map) // remap our new struct
			{
				// move the last element to the first
				loc.newPos = loc.item;
				loc.last = Replace(ListLast(loc.newPos, "["), "]", "");
				if (IsNumeric(loc.last))
				{
					loc.newPos = ListDeleteAt(loc.newPos, ListLen(loc.newPos, "["), "[");
					loc.newPos = ListPrepend(loc.newPos, "[" & loc.last, "]");
				}
				else
				{
					loc.newPos = ListPrepend(loc.newPos, "[1", "]");
				}
				loc.map[loc.item].newPos = ListToArray(Replace(loc.newPos, "]", "", "all"), "[", false);
				
				// loop through the position array and build our new struct
				loc.struct = loc.value;
				loc.jEnd = ArrayLen(loc.map[loc.item].newPos);
				for (loc.j = 1; loc.j lte loc.jEnd; loc.j++)
				{
					if (!StructKeyExists(loc.struct, loc.map[loc.item].newPos[loc.j]))
						loc.struct[loc.map[loc.item].newPos[loc.j]] = {};
					if (loc.j != loc.jEnd)
						loc.struct = loc.struct[loc.map[loc.item].newPos[loc.j]];
					else
						loc.struct[loc.map[loc.item].newPos[loc.j]] = loc.map[loc.item].value;
				}
			}
		}
	</cfscript>
</cffunction>

<cffunction name="$findMatchingRoute" returntype="struct" access="public" output="false">
