<!--- Check to see which mode we are executing. ---> <cfif (thistag.executionMode eq "start")> <!--- Param tag attributes. ---> <!--- This is the "index" variable name that the caller will be using to store the iterative value. I went with item rather than index only because this can iterate over non-array items. ---> <cfparam name="attributes.item" type="variablename" /> <!--- This it the collection that we are iterating over. This might be an array, a struct, etc. ---> <cfparam name="attributes.collection" type="any" /> <!--- Start tag logic. ---> <!--- Check to see if we have a valid collection type. ---> <cfif ( !isStruct( attributes.collection ) && !isArray( attributes.collection ) )> <!--- Invalid collection type. Throw error. ---> <cfthrow type="UnsupportedCollectionType" message="Unsupported collection type." detail="Unsupported collection type. Currently, only structures and arrays are supported." /> </cfif> <!--- ASSERT: At this point, we know that our collection is a valid type for what this custom tag can handle. ---> <!--- Check to see if we have any values in our collection. If not, then we can immediately break out of the custom tag. ---> <cfif ( ( isStruct( attributes.collection ) && !structCount( attributes.collection ) ) || ( isArray( attributes.collection ) && !arrayLen( attributes.collection ) ))> <!--- Collection is empty. ---> <cfexit method="exittag" /> </cfif> <!--- ASSERT: At this point, we know that our collection has at least ONE item in the collection. ---> <!--- Create a variable to handle the loop iteration. ---> <cfset iterationIndex = 1 /> <!--- Check to see what kind of data collection we have. Each collection type will have to be handled slightly differently. ---> <cfif isStruct( attributes.collection )> <!--- Get the array of struct keys. ---> <cfset keys = structKeyArray( attributes.collection ) /> <!--- Set up the item in the caller for the first collection iteration. ---> <cfset caller[ attributes.item ] = { index = iterationIndex, key = keys[ iterationIndex ], value = attributes.collection[ keys[ iterationIndex ] ], collectionType = "struct" } /> <cfelseif isArray( attributes.collection )> <!--- Set up the item in the caller for the first collection iteration. ---> <cfset caller[ attributes.item ] = { index = iterationIndex, key = iterationIndex, value = attributes.collection[ iterationIndex ], collectionType = "array" } /> </cfif> <cfelse> <!--- Increment the iteration index for the next loop. ---> <cfset iterationIndex++ /> <!--- At this point, we have to check to see if our collection requires any more iterations. For this, we again will need to see what type of collection we are walking. ---> <cfif ( ( isStruct( attributes.collection ) && (arrayLen( keys ) LT iterationIndex) ) || ( isArray( attributes.collection ) && (arrayLen( attributes.collection ) LT iterationIndex) ))> <!--- We are done walking the collection. ---> <cfexit method="exittag" /> </cfif> <!--- ASSERT: At this point, we know that we have at least one more collection iteration remaining. ---> <!--- Check to see which type of collection we have and therefore how to update the item key for the next iteration. ---> <cfif isStruct( attributes.collection )> <!--- Set up the item in the caller for the next collection iteration. ---> <cfset caller[ attributes.item ] = { index = iterationIndex, key = keys[ iterationIndex ], value = attributes.collection[ keys[ iterationIndex ] ], collectionType = "struct" } /> <cfelseif isArray( attributes.collection )> <!--- Set up the item in the caller for the next collection iteration. ---> <cfset caller[ attributes.item ] = { index = iterationIndex, key = iterationIndex, value = attributes.collection[ iterationIndex ], collectionType = "array" } /> </cfif> <!--- If we've gotten this far, it's time to loop back to the tag body with the new item value. ---> <cfexit method="loop" /> </cfif>