ColdFusion jQuery Cycle Slideshow Custom Tag

I was putting together yet another slick little jQuery Cycle slideshow tonight, and decided to make it dynamic.

I wanted a solution that would take a folder full of images, size them to a given max width and height, and set up the cycle slideshow, all on the fly, while keeping it easy to slide another image or two in any time, or tweak the cycle slideshow settings.

Here's my attempt. I would greatly appreciate any testing and feedback - I wired in a handful of options but I am sure there's more that can be done with it.

NOTE: Code updated 2009-06-22... not sure what's changed exactly from the version posted originally, it has been a while and I don't feel like doing file compare at the moment, but I've tweaked a few things in the course of using it myself the last 6 months or so. If you've had problems , try this version:


<cfsilent>
    
    <!---
jQuery Cycle Custom Tag
Michael Evangelista : www.mredesign.com
Returns self-contained slideshow using jQuery cycle plugin

// NOTE: image resizing requires CF8
// This tag assumes you have a folder called js/ in the site root, with your
// jquery file and your jquery cycle file... adjust <script> src path below

// SEE PARAMETERS AND COMMENTS BELOW
// You can pass in a number of options, including
- randomize
- autowrap (set the w/h of the container to the largest image w and h)
- autosize (scale the images down to fit the size given - overrides autowrap, above)
// TIP: For max performance with the resizing option,
// run it once after adding any new images with autosize="1",
// then change it to autosize="0", autowrap="1" (see below for examples)

// The cycle plugin page is here: http://malsup.com/jquery/cycle/
// the cycle plugin has *lots* of options !!
// http://www.malsup.com/jquery/cycle/options.html

// CSS sets the slideshow UL to 'display:none', and jQuery shows it
// if there is no javascript, the slideshow is not shown at all

DIRECTIONS FOR USE:
//1//
put your jquery and cycle.js files in your /js/ folder

//2//
import the custom tag (change the path - default is siteroot/tags/ )

<cfimport taglib="tags" prefix="sitetags">


//3//
put the cycle in your page!

// SEE PARAMETERS BELOW FOR ALL OPTIONS //

///// example 1 /////
// this will make a box container scaled to fit your tallest and widest image
// images are not resized
// images are in siteroot/img/shapes
// with a nice fade effect (default, not specified)
// and a 6 second pause on each slide
// using the jQuery native 'slow' speed
<sitetags:cycleslideshow
autowrap="1"
autosize="0"
ssFolder = "img/shapes"
cycleOptions="{
timeout: 6000,
    speed: 'slow'
}"
>

///// example 2 /////
// this will resize your images to 280x280 max
// with a shuffle effect, a 2 second pause on each image
// and a 1 second pause before the first transition starts
// pause:true causes it to stop rotation on mouseover
// images are in siteroot/slideshow
// images are scaled to fit inside a 280x280 box

<sitetags:cycleslideshow
ssW = "280"
ssH = "280"
ssFolder = "slideshow"
cycleOptions="{
fx: 'shuffle',
    pause: true,
    timeout: 2000,
delay: 1000,
}">

///// example 3 /////
// images are resized, scaled to fit inside a 420x420 box
// with a 3 second pause before the first transition starts
// and a 1/2 second delay on each image
// images are in siteroot/img/slideshow

<sitetags:cycleslideshow
ssW = "420"
ssH = "420"
ssFolder = "img/slideshow"
cycleOptions="{
    timeout: 500,
delay: 3000,
    speed: 'slow'
}">

// lather, rinse , repeat

--->


<!--- SET UP ATTRIBUTES --->
<!--- all of these attributes can be passed in,
or just change the values here for single use --->

<!--- see below each line for explanation --->
<cfparam name="attributes.ssFolder" default="slideshow">
<!--- relative path to the folder where the images are stored--->
<cfparam name="attributes.ssPath" default="#expandPath(attributes.ssFolder)#\">
<!--- the full server path for the (created by the folder path above, no need to change) --->
<cfparam name="attributes.autosize" default="1">
<!--- resize the images to fit the sizes given below (0/1) --->
<cfparam name="attributes.ssW" default="360">
<!--- max width of images (numeric, pixels)--->
<cfparam name="attributes.ssH" default="360">
<!--- max height of images (numeric, pixels)--->
<cfparam name="attributes.cycleOptions" default="">
<!--- options to include in cycle javascript - see jquery cycle plugin page --->
<cfparam name="attributes.divclass" default="cycleWrap">
<!--- the class to give the 'div' container --->
<cfparam name="attributes.listclass" default="cycleList">
<!--- the class to give the <ul> list --->
<cfparam name="attributes.randomize" default="1">
<!--- make the display random (0/1) --->
<cfparam name="attributes.autowrap" default="1">
<!--- make the container the size of the largest image in the folder (0/1)
( must set 'autosize' to 0)--->

<cfparam name="attributes.imgAlt" default="">
<!--- alt tag for your images --->
<cfparam name="attributes.addjQuery" default="0">
<!--- if you already have jQuery in your page, this should be 0. If not, make it 1 --->
<!--- to see the active variables displayed on the page, put "?showvars=[password]" in your url, using the password you create here: --->
<!--- set the showvars password you want to use for this page --->
<cfset showvarspw = "sscdebug">

<!--- SEE NOTES above for info about js files, etc --->
<!--- change the path in these <script> tags if your js folder is different --->
<cfsavecontent variable="tp.jsCode">
<cfif attributes.addjQuery is not 0><script type="text/javascript" src="js/jquery-1.2.6.pack.js"></script></cfif>
<script type="text/javascript" src="js/jquery.cycle.pack.js"></script>
</cfsavecontent>

<!--- ///// NO NEED TO EDIT BEYOND HERE /////--->
<cfparam name="tp.errorlist" default="">

<!--- insert all attributes to tp struct for simpler use --->
<cfparam name="tp.placeholder" default="ok">
<cfloop collection="#attributes#" item="attrvar">
<cfoutput>
<cfset colname="#attrvar#">
<cfset colval= "#attributes[attrvar]#">
<cfset structInsert(tp,colname,colval)>
</cfoutput>
</cfloop>

</cfsilent>


<!--- START SLIDESHOW --->
<!--- if the directory exists --->
<cfif directoryExists(tp.ssPath)>
<cftry>
<!--- get the images --->
<cfdirectory action="List"
    directory="#tp.ssPath#"
    name="listImages"
    sort="name asc"
filter="*.jpg|*.gif|*.png|*jpeg"
>

<!--- set up list of images --->
<cfset tp.imgList = valueList(listImages.name)>
<cfset tp.imgCt = listLen(tp.imgList)>
<!--- if randomizing --->
<cfif tp.randomize>
<cfset tp.randList = ''>
<cfset loopList = "#tp.imglist#">
<cfloop from="1" to="#tp.imgct#" index="cc">
<cfset loopCt = listLen(loopList)>
<cfset imgPos = randRange(1,loopCt)>
<cfset imgVal = listGetAt(loopList,imgPos,',')>
<!--- DEBUG --->
<!--- <cfdump var="#imgVal#"> : order <cfdump var="#imgPos#"><br /> --->
<cfset tp.randList = listAppend(tp.randList,imgVal)>
<cfset looplist = listDeleteAt(looplist,imgPos)>
<!--- DEBUG --->
<!--- <br /><br /><cfdump var="#looplist#"><br /><br /> --->
</cfloop>
<cfset tp.imgList = "#tp.randList#">
</cfif>
<cfcatch>
    <!--- if any errors --->
<cfset tp.errorlist = listAppend(tp.errorlist, 'Error reading images folder : #cfcatch.message#')>
</cfcatch>
</cftry>




<!--- set up the markup --->
<cfsavecontent variable="ssHtml">
<div class="<cfoutput>#tp.divClass#</cfoutput>">
<ul class="<cfoutput>#tp.listClass#</cfoutput>">
<cfloop list="#tp.imgList#" index="ii">
<cfoutput><li><img src="#tp.ssFolder#/#ii#"<cfif tp.imgalt is not ''> alt="#tp.imgAlt#"</cfif>></li></cfoutput>
            <cfset imgPath = tp.ssPath & '#ii#'>
            
            <cftry>
            <!--- Get Info about each image --->
            <cfimage action="INFO" source="#imgPath#" structname="imgInfo" />
        <!--- if autosizing, make a copy of the image, scaled to fit (only size down, not up)--->
        <cfif tp.autosize is not 0 AND (tp.ssw lt imgInfo.width OR tp.ssh lt imgInfo.height)>
            <!--- first put a copy in /orig/ --->
            <cfset newFolder = "#tp.ssPath#/orig/">
            <cfif not directoryExists(newFolder)>    
                <cfdirectory action="create" directory="#newFolder#">
            </cfif>
                <!--- if we don't have an orig version yet, make one --->
                <cfif not fileExists('#newFolder#/#ii#')>
                    <cffile action="copy" source="#imgPath#" destination="#newFolder#/#ii#">
                </cfif>
                <!--- now resize the original image--->
                <cfimage source="#imgInfo.source#" name="newImg">
                <cfset ImageSetAntialiasing(newImg)>
                <cfset ImageScaleToFit(newImg,tp.ssW,tp.ssH)>
                <cfset ImageWrite(newImg)>
        </cfif>
        
        <!--- if autowrapping, get the size of each image and stick with the largest in each direction --->
        <cfif tp.autowrap AND NOT tp.autosize>
                <cfif imgInfo.height gt tp.ssH>
                <cfset tp.ssH = imgInfo.height>
                </cfif>
                <cfif imgInfo.width gt tp.ssW>
                <cfset tp.ssW = imgInfo.width>
                </cfif>
        </cfif>
        
        <cfcatch>
        <cfset tp.errorlist = listAppend(tp.errorlist, 'Image Information Unavailable (#ii#) : #cfcatch.message#')>
        </cfcatch>
        
        </cftry>
        
</cfloop>
</ul>
</div>
</cfsavecontent>


<!--- Show the Active Variables and other Info --->
<!--- don't edit this param --->
<cfparam name="url.showvars" default="none">

<cfif url.showvars is "#showvarspw#">
<cfdump var="#listImages#" label="List Images:">
<br />
<cfdump var="#tp#">
<br />
<cfabort >
</cfif>

<!--- set up the stuff for the page head --->
<cfsavecontent variable="hcode">
<cfoutput>#tp.jsCode#</cfoutput>
<!--- initialize cycle --->
<script type="text/javascript">
$(document).ready(function(){
    var cycleEl = $('ul.<cfoutput>#tp.listClass#</cfoutput>');
    $(cycleEl).show().cycle(<cfif tp.cycleOptions neq ''><cfoutput>#tp.cycleoptions#</cfoutput></cfif>);
});
</script>
<style type="text/css">
<cfoutput>
ul.#tp.listClass#{
list-style:none;
list-style-indent:0;
list-style-position:inside;
display:none;
}

.#tp.listClass# li{
width: #tp.ssW#px;
height: #tp.ssH#px;
list-style:none;
text-indent:0;
margin:0 0 0 -48px;
}

div.#tp.divclass#{
width: #tp.ssW#px;
height: #tp.ssH#px;
float:right;
margin:0;
}

/* --- any styles you want can go here --- */
</cfoutput>
</style>
</cfsavecontent>
<cfhtmlhead text="#hcode#">


<!--- CFDUMP - for troubleshooting ::
uncomment to see if you are catching any images! --->

<!--- <cfdump var="#listImages#"> --->
<cfelse>
<!--- if the directory is not found --->
<cfset tp.errorlist = listAppend(tp.errorlist, 'Images folder does not exist')>
</cfif>

<!--- if we have errors --->
<cfif listLen(tp.errorList)>
<cfloop list="#tp.errorList#" index="ee">
<p class="errorMessage"><cfoutput>#ee#</cfoutput></p>
</cfloop>
<cfelse>
<cfoutput>#ssHtml#</cfoutput>
</cfif>

Comments (Comment Moderation is enabled. Your comment will not appear until approved.)
Hi,
I have tried, but all that is happening is a photo gets moved from the folder called /slideshow to /slideshow/org folder....
what could I be doing rong..??
Greets.,
W.A.
# Author Willem-Alexander | 6/18/09 5:50 PM
Hi Willem!

There are probably a number of ways things could get funky ... if the image is being copied to 'orig', that shows something's happening but I'd need to see it in the wild to be much help beyond that. Do you have a link? You can also email me your gallery tag, images and cf files all zipped up and i can try on my server.
# Author Michael Evangelista | 6/19/09 1:56 PM
I've tried, but I didn't get the slide show working as shown on the demo. Not knowing why went wrong.

The calling page is called "myslideshow.cfm", as shown below -
=============================================
<cfimport taglib="tags" prefix="sitetags">
<sitetags:cycleslideshow
autowrap="1"
autosize="0"
ssFolder = "img/shapes"
cycleOptions="{
timeout: 2000,speed: 'slow'
}"
>
=============================================
The custom tag is called "cycleslideshow.cfm" under the subfolder called "tags". I used

<cfparam name="attributes.addjQuery" default="1">
<cfsavecontent variable="tp.jsCode">
<cfif attributes.addjQuery is not 0>
<script type="text/javascript" src="js/jquery-1.3.1.js"></script>
</cfif>
<script type="text/javascript" src="js/jquery.cycle.all.js"></script>
</cfsavecontent>

where
js/jquery-1.3.1.js and js/jquery.cycle.all.js" are downloaded from http://www.malsup.com/jquery/cycle/download.html

=============================
All I got is a list of 3 photos (use the same photos as shown in demo) lined up vertically, each time I refreshed, I got photos rearranged, but still see all 3 pictures without doing the slide shows.

What did I do wrong?

Thanks

Jerry
# Author Jerry | 6/22/09 1:06 AM
@jerry - got a link? or how about the 'view source' from your browser, on the page with the images showing?

Sounds like , for whatever reason, the javascript isn't firing correctly, or perhaps the css is missing... I'd have to see more to have much of a clue.

Alternately (or additionally), please email a zip file containing your cfm page, the tag, your original images... the works. Send it to gowestweb at gmail . Your help testing this is appreciated so I sure don't mind helping you get it right.

Side note: this is my highest-viewed blog post, over 7,500 views since posting! I sure hope this thing actually works for people... I use it all the time myself.
# Author Michael Evangelista | 6/22/09 3:08 AM
@jerry - got your email, unzipped it to my CF local server, and voila, instant slideshow. Didn't do a thing but browse to the page (in both FF and IE without issues).

As a test, I added the parameters
autosize="1"
imgW="120"
and sure enough , the images were sized down to 120px, originals saved in a newly-created 'orig' folder.

In other words, it seems to be working fine! The problem you describe (just an unstyled ul of images) would be the result if javascript was disabled and no css applied.

Could it be the paths to the js and css are off? Try your page in firefox and use the web developer toolbar's "information > view javascript" option to see any linked js files and whether they show the actual script, or a 404 error.

As for the 'cycleWrap' class - whatever class you give the slideshow in the attributes will be dynamically styled using the css towards the end of the 'cycleslideshow.cfm' file , along with the jQuery code to make the actual slideshow based on your parameters...

see below:

<!--- set up the stuff for the page head --->
<cfsavecontent variable="hcode">
<cfoutput>#tp.jsCode#</cfoutput>
<!--- initialize cycle --->
<script type="text/javascript">
$(document).ready(function(){
var cycleEl = $('ul.<cfoutput>#tp.listClass#</cfoutput>');
$(cycleEl).show().cycle(<cfif tp.cycleOptions neq ''><cfoutput>#tp.cycleoptions#</cfoutput></cfif>);
});
</script>
<style type="text/css">
<cfoutput>
ul.#tp.listClass#{
list-style:none;
list-style-indent:0;
list-style-position:inside;
display:none;
}

.#tp.listClass# li{
width: #tp.ssW#px;
height: #tp.ssH#px;
list-style:none;
text-indent:0;
margin:0 0 0 -48px;
}

div.#tp.divclass#{
width: #tp.ssW#px;
height: #tp.ssH#px;
float:right;
margin:0;
}

/* --- any styles you want can go here --- */
</cfoutput>
</style>
</cfsavecontent>
<cfhtmlhead text="#hcode#">
# Author Michael Evangelista | 6/25/09 6:05 PM
How would you change the fx: parameter dynamically. I would like to store that parameter in a database record along with other variables that define the page layout and get the variable from a CF query then input the fx: parameter dynamically
# Author William | 11/7/09 5:39 AM
I posted a previous comment. I figure out the answer by looking closer at your code. Thanks. Great scripting
# Author William | 11/7/09 6:39 AM
blogcfc 5.9.1.002 by raymond camden
contact michael evangelista