Beware of Implicit Structs Bug in ColdFusion 9,0,1

This has been driving me nuts and I'm not sure how well documented this is, but I've stumbled across a bug in ColdFusion 9,0,1,274733 where implicit structs can end up as 'undefined' and crash your application.

I was working through an example from jQuery in Action, Second Edition (you know, because I was trying to figure something else out) when this happened to me again and I thought I would go ahead and share a simple way for others to test and let me know if it's happening to them.

Create a page called test.cfm and drop this code in it, point your browser to it and you'll probably see an error of 'Variable TERMS is undefined'.


<cfscript>
    if ( not IsDefined('term') ) {
        term = 'unknown';
    };
    term = lcase(term);

    terms = {
        'oil-tanned' = 'A method of leather tanning in which oils or fats are used to cure the leather. Such leather is usually very supple and has a matte or "oily" finish and is not generally polishable.',
        'full-grain' = 'Leather which has not been altered beyond hair removal. Full-grain leather is the most genuine type of leather, as it retains all of the original texture and markings of the original hide.',
        'vibram' = 'A brand of boot and shoe sole created by Vitale Bramani in the 1930s, orginally for climbing boots. They are easily identified by the distinctive yellow Vibram® octagon. The Vibram® brand is recognized worldwide as the leader in high performance soling products for outdoor, dress casual, and service footwear.',
        'goodyear welt' = 'The Goodyear welt is a method of attaching the sole of a shoe to the upper that is hand-stitched and allows multiple sole replacements, extending the life of the footwear.',
        'cambrelle' = ' A non-woven synthetic fabric used primarily as a lining for shoes, boots and slippers.',
        'cordura' = 'A certified fabric from INVISTA used in a wide range of products from luggage and backpacks to boots, to military wear and performance apparel. Cordura® is resistant to abrasions, tears and scuffs.',
        'gore-tex' = 'A water-proof and breathable fabric that offers superior insulating abilities in a light-weight fabric.',
        'stitch-down' = 'A method of boot construction that helps seal the boot against dirt, mud, and water and maximizes flexibility.',
        'unknown' = 'Unknown term.'
    };
</cfscript>
<cfoutput>#terms[term]#</cfoutput>

The funny thing is, if I move the code at the top of the page below the implicit struct, the page loads fine! Here's some code that should work now:


<cfscript>    
    terms = {
        'oil-tanned' = 'A method of leather tanning in which oils or fats are used to cure the leather. Such leather is usually very supple and has a matte or "oily" finish and is not generally polishable.',
        'full-grain' = 'Leather which has not been altered beyond hair removal. Full-grain leather is the most genuine type of leather, as it retains all of the original texture and markings of the original hide.',
        'vibram' = 'A brand of boot and shoe sole created by Vitale Bramani in the 1930s, orginally for climbing boots. They are easily identified by the distinctive yellow Vibram® octagon. The Vibram® brand is recognized worldwide as the leader in high performance soling products for outdoor, dress casual, and service footwear.',
        'goodyear welt' = 'The Goodyear welt is a method of attaching the sole of a shoe to the upper that is hand-stitched and allows multiple sole replacements, extending the life of the footwear.',
        'cambrelle' = ' A non-woven synthetic fabric used primarily as a lining for shoes, boots and slippers.',
        'cordura' = 'A certified fabric from INVISTA used in a wide range of products from luggage and backpacks to boots, to military wear and performance apparel. Cordura® is resistant to abrasions, tears and scuffs.',
        'gore-tex' = 'A water-proof and breathable fabric that offers superior insulating abilities in a light-weight fabric.',
        'stitch-down' = 'A method of boot construction that helps seal the boot against dirt, mud, and water and maximizes flexibility.',
        'unknown' = 'Unknown term.'
    };

    if ( not IsDefined('term') ) {
        term = 'unknown';
    };
    term = lcase(term);
</cfscript>
<cfoutput>#terms[term]#</cfoutput>

If I'm doing something wrong, please don't hesitate to point it out to me. Thanks!

cf.Objective() 2011 Pricing Announced

Yo! ColdFusion peeps! It's hard to believe it, but cf.Objective() 2011, the world's only enterprise engineering conference for ColdFusion, will be here before you know it. The conference will be held in sunny downtown Minneapolis, MN and runs May 12-14, 2011. In fact, pricing has just been announced and is available on the cf.Objective() web site. Oh, and register by February 14th, 2011 and save an extra $100!

So, when you prepare your budget for next year, if you haven't already done so, don't forget to include cf.Objective() 2011.

Hey, CFUnited Attendees ...

If you have EVER been an attendee of CFUnited, you are eligible for a $100 Alumni Discount! How cool is that?

Vote Now!

While you're at it, tell cf.Objective() what you're interested in learning about by completing a brief Topic Suggestion Survey >>

Hope to see you there!

Using jQuery to Pass an Index Value to a Shadowbox.js Gallery

I ran into a bit of a hair-pulling incident recently and I really couldn't find any help on the web with this one. So I thought I'd share my frustration and the solution I came up with in case anyone else runs into a similar problem ... or heck, maybe you've got a better solution for me. In short, I needed to be able to pass an index value to Shadowbox.js when a gallery was launched so that it would begin at the desired position. However, I personally find it more interesting to understand the evoution of the problem and the eventual solution. If you're not like me, feel free to jump down to the jQuery and HTML code below.

The Problem Challenge

Well, this all started out when one of my clients wanted one of those fancy-dancy "hover-over-a-group-of-thumbnails-and-swap-a-medium-sized-image" thing-a-ma-bobs. Trust me, this was not the challenge ... using a few lines of jQuery and fancy-dancy-be-done.

Then, after looking things over a bit, the client wanted to be able to click on the thumbnail and open a modal window of the original, much larger, image. Again, no worries ... using a bit of Shadowbox.js magic, and client-be-happy.

Finally, after reviewing my jQuery/Shadowbox/programming magic, the client was happy ... but they wanted one more thing ... to be able to click on the medium-sized image and have it launch a modal window of the original, much larger, image too. So, after a little extra thought and tweaking around, I got it working ... well, sort of.

Here's the Deal

Everything worked just fine except for the fact that if I enabled the 'continuous' attribute so that people could click "Next" and "Previous" links, something odd occurred.

If I clicked a thumbnail, the Shadowbox opened and did its thing as expected. If there were four (4) image thumbnails, then when the modal window opened, the "Next" and "Previous" links would cycle through four (4) images. But when I clicked the medium-sized image, the larger version of the image would appear twice in the modal window for a total of five (5) images.

Anyway, I realized fairly quickly that by default, Shadowbox creates a cached array of things such as all links with a 'rel' attribute of 'shadowbox' when the page loads. You can easily override this feature in the Shadowbox init() method by setting the 'skipSetup' attribute to true. But that's not what I really needed, or wanted, to do. I already had the thumbnails working and just wanted to get the 'Medium' sized image to also open the Shadowbox.

The My Solution

Ultimately, I needed a way to keep track of the index of each Shadowbox link element so that I could use it when calling a Shadowbox function I hadn't used before called "Shadowbox.open()." I knew there just had to be a way of passing the index value to Shadowbox so that way when the modal window would open, it would just cue up to the cached index in Shadowbox.

So I created a custom attribute for the "a" link of the thumbnail images called "idx" and since I was already outputting the thumbnails from a query, this was pretty easy to do. However, since I was using a ColdFusion loop to output an array of the images, I couldn't just use the index value of the loop since ColdFusion arrays start at 1 while just about every other programming language, including JavaScript, have their arrays start at zero (0). So for you ColdFusion peeps, just remember you'll want to add your own "counter" variable that starts at zero and then increment it at the end of each iteration of the loop.

Once I had an attribute I could access using jQuery, I knew I was in business. The next thing I needed to find out was how to pass this index value to Shadowbox. Again, nothing on the web helped me out here. I did stumble across someone's post in the Shadowbox forum about how they modified the source code to accommodate something like this, but I chose not to go that route. In addition, this was a site using Mura CMS and I really didn't want to make any alterations to any included third-party code if I really didn't have too.

After studying the Shadowbox source code for a bit, I found this line of code in the Shadowbox.open() method (around line 2122):


// is it a link?
if(isLink(obj)){
    if(typeof obj.shadowboxCacheKey == 'undefined' || typeof cache[obj.shadowboxCacheKey] == 'undefined'){
        // link element that hasn't been set up before
        // create an object on-the-fly
        obj = this.buildCacheObj(obj, opts);
    }else{
        // link element that has been set up before, get from cache
        obj = cache[obj.shadowboxCacheKey];
    }
}

Notice the "shadowboxCacheKey" bit? Bingo! Now I knew all I needed to do was tweak my jQuery code to add this sweet little attribute to the link and then call the Shadowbox.open() method along with some options to tell Shadowbox which gallery I wanted to use and at what index to begin! Can you tell I was a little excited?

So instead of continuing to bore the crap out of you, I'll just go ahead and show you what I came up with.

The jQuery


$(document).ready(function() {
    $(".imageThumb").hover(
        function() { // handlerIn
            var mediumImage = $(this).find('a').attr('medium');
            var imgLink = $(this).find('a').attr('href');
            // this holds the 'Shadowbox' cacheKey index value!
            var imgIndex = $(this).find('a').attr('idx');
            $('#swapImg').attr({src:mediumImage}).fadeIn(800);
            $('#swapLink').attr({href:imgLink,idx:imgIndex});
            return false;
        }
    );

    $("#featuredImage >
a#swapLink").click(function(event) {
        event.preventDefault();
        // grab the 'Shadowbox' cacheKey index value
        var idx = $('#featuredImage').find('a').attr('idx');
        // now add the index key to the link so that when we call open, it knows this link already exists
        this.shadowboxCacheKey = idx;
        Shadowbox.open(this,{gallery:"products",continuous:true});
    });

});

The HTML


<div id="imageThumbs">
    <div class="imageThumb"><a rel="shadowbox[products];options={continuous:true};" idx="0" href="img1.jpg" title="" medium="img1-med.jpg"><img src="img1-thumb.jpg" alt="" width="70" height="70" /></a></div>
    <div class="imageThumb"><a rel="shadowbox[products];options={continuous:true};" idx="1" href="img2.jpg" title="" medium="img2-med.jpg"><img src="img2-thumb.jpg" alt="" width="70" height="70" /></a></div>
    <div class="imageThumb"><a rel="shadowbox[products];options={continuous:true};" idx="2" href="img3.jpg" title="" medium="img3-med.jpg"><img src="img3-thumb.jpg" alt="" width="70" height="70" /></a></div>
    <div class="imageThumb"><a rel="shadowbox[products];options={continuous:true};" idx="3" href="img4.jpg" title="" medium="img4-med.jpg"><img src="img4-thumb.jpg" alt="" width="70" height="70" /></a></div>
</div>
<div id="productImages">
    <div id="featuredImage"><a idx="0" id="swapLink" href="img1.jpg" ><img id="swapImg" src="img1-med.jpg" border="0" alt="" /></a></div>
</div>

That's it! I hope this helps someone else and saves some hair pulling. Enjoy!

© 2024, Stephen J. Withington, Jr.  |  Hosted by Hostek.com

Creative Commons License   |   This work is licensed under a Creative Commons Attribution 3.0 Unported License.