IFrames and cross-domain security, part 3

In 2008 I posted a quick writeup on how I dealt with cross-domain security issues for some of my e-learning courseware. Since then, I’ve had a lot of people contact me with various questions and requests for a working example.

Tonight I decided to revisit the topic and whip up some quick example files. To my chagrin, I discovered a load of typos in my original post. Yikes! No wonder people wanted to see some working examples.

Well, ask no more, here they are:

The explanation provided in the previous post is still valid, just refer to these new sample files instead of the code in that post’s examples. (I will update that post with new code examples soon.)

Remember, the proxy.html file must reside on the same domain as the parent frame. This solution will not work if you don’t have FTP access to both domains, since you need to place support scripts on both domains.

Comparing and cloning objects in JavaScript

Here’s a handy way to determine if two JavaScript objects are identical without using a framework like jQuery or MooTools:


var compare_objects = function (obj1, obj2){

    var parameter_name;
    
    var compare = function(objA, objB, param){
        
        var param_objA = objA[param],
            param_objB = (typeof objB[param] === "undefined") ? false : objB[param];
        
        switch(typeof objA[param]){
            case "object": return (compare_objects(param_objA, param_objB));
            case "function": return (param_objA.toString() === param_objB.toString());
            default: return (param_objA === param_objB);
        }
        
    };
    
    for(parameter_name in obj1){
        if(typeof obj2[parameter_name] === "undefined" || !compare(obj1, obj2, parameter_name)){
            return false;
        }
    }

    for(parameter_name in obj2){
        if(typeof obj1[parameter_name] === "undefined" || !compare(obj1, obj2, parameter_name)){
            return false;
        }        
    }

    return true;

};

Here’s an easy way to clone a JavaScript object:


var clone_object = function (original_obj) {
    var new_obj = {};
    for(var param in original_obj) {
        if(original_obj.hasOwnProperty(param)){
            if(typeof(original_obj[param]) === "object"){
                new_obj[param] = clone_object(original_obj[param]);
            } else {
                new_obj[param] = original_obj[param];
            }
        }
    }
    return new_obj;
};

A real-world example of the two functions:


var object_1 = { fruit: "apple", tree: "dogwood", number: 3 };
var object_2 = { fruit: "apple", tree: "dogwood", number: 3, cartoons: { anime: "robotech", fantasy: "he-man" } };
var object_3 = clone_object(object_2);

console.log("objects 1 and 2 match? " +compare_objects(object_1, object_2));
//displays false;

console.log("objects 2 and 3 match? " +compare_objects(object_2, object_3));
//displays true

Bear in mind both of these functions are recursive, which means the larger the object, the slower the performance.

For Your Reading Pleasure: EasyCaptions

Introducing EasyCaptions: A simple system for adding captions and an interactive transcript to online videos. EasyCaptions uses progressive enhancement to provide the best possible experience for all visitors, regardless of their browser’s JavaScript, HTML5 or Flash support.
Demonstration

Background

I don’t produce much video these days, but as a web surfer I often encounter other people’s videos, and was recently impressed by two video implementations: a TED Talks video page, and an HTML5 video demo produced by Bruce Lawson.

The TED Talks page had a great feature I’d never really seen anywhere else: an interactive transcript of the video that you can read and click. For example, you can click the third sentence of the transcript and the video will jump to that point.

Bruce Lawson’s demo illustrated a dead-simple way to add captions to an HTML5 video using just a wee bit of HTML markup and JavaScript.

Both of these pages shared a unique attribute: they stored complete transcripts of the videos in the markup of the HTML page itself, NOT in an external XML file, as is most commonly seen. What’s the big deal about inline transcripts, you ask? Well, today’s most common captioning options require placing your caption text in an external XML file that loads via ActionScript or JavaScript/AJAX. Both the TED page and Bruce’s demo eschew that approach and place the full transcript in the HTML. This means the transcript is always available to the visitor, regardless of that browser’s support for HTML5, JavaScript or Flash Player.

In both cases, the transcript had been marked up as paragraphs, with extra markup denoting phrases that align with the video’s timecode. The TED site used a tags to mark each phrase, with inline JavaScript triggering the playback of the clicked link. This is functional when JavaScript is enabled, but fails when JavaScript is disabled, leaving you with a ton of bloated markup that doesn’t work:


<p>
<a href="#" class="transcriptLink" onclick="seekVideo(0); return false;">One way to change our genes is to make new ones,</a> 
<a href="#" class="transcriptLink" onclick="seekVideo(2000); return false;">as Craig Venter has so elegantly shown.</a>
</p>

Bruce’s span tags, on the other hand, are semantically sound — a span is a neutral, unobtrusive inline element meant to be a child of a block element such as p. Perfect. But wait, there’s more! Since this was an HTML5 demo, Bruce took advantage of the new data attribute that can be used on just about any HTML element. (The short version is: you can create any custom attribute you want, so long as the name begins with data-.) Bruce decided to create two attributes for each span: data-begin, which indicates (in seconds) when the phrase starts in the video, and data-end, which indicates when that phrase has ended in the video. Simple as can be, and extremely efficient:


<p>
<span data-begin=1 data-end=6>Hi, my name's Dr Archimedes Einstein 
and I'm a Dctor of Science at the University of Science</span>
</p>

Enter: EasyCaptions

I was incredibly inspired by these two pages and decided to combine their features, creating a new system I call EasyCaptions. The goal of EasyCaptions is to make it as simple as possible to add useful captions to your online videos. I want to eliminate the headaches of captioning as well as the excuses (too hard, confusing, etc.).

What EasyCaptions does:

  • Dynamically generates a div under your video that will display caption text (the div‘s style and positioning is completely configurable via CSS).
  • Dynamically makes each span in your transcript clickable, jumping to that point in the video. This is done via progressive enhancement and event delegation, leaving your markup clean and avoiding heavy-handed use of onclick.
  • Works out-of-the-box with HTML5.
  • Includes HTML5 video support detection, enabling you to use a Flash-based fallback if you desire.
  • Provides a hook for Flash-based fallback systems, enabling the captions and transcript to behave identically to the HTML5 version.

What you’ll need to do:

  • Type up the transcript of your video, using <span> tags to wrap each phrase, just like Bruce’s example above.
  • Place the transcript in a container element with a unique ID (such as div id="transcript").
  • Add a teeny bit of JavaScript (about 4 lines).

The final product. Successfully tested in Firefox, Safari, Opera, and Internet Explorer (IE and older versions of the other browsers all use a Flash Fallback if provided).

Peruse the test suite to get an idea of how flexible the system is.

The documentation and downloads are located on the EasyCaptions page.

Providing the same UI across browsers

As a web designer and e-learning course developer, I often grapple with the notion of making my products appear the same in every browser. I’m not opposed to the idea that you should make your work look (almost) identical in every browser. After all, you’re promoting a specific design aesthetic and the last thing you want is for a browser to dictate what you can and can’t do, especially browsers you don’t even like. (I’m looking at you, IE6.)

But it’s never as simple as we’d like. Internet Explorer has that crazy CSS box model. Older versions of Firefox and Safari don’t support border-radius or RGBa.  Mac browsers use completely different scrollbars and form elements than Windows and Linux browsers. For that matter, scrollbars and form elements in Windows 2000 look completely different than XP, Vista, and Windows 7.

This is one of the reasons Flash has become so popular — it allows designers to standardize their RIA‘s UI elements across browsers.

The latest salvo in the war for controlling your browser’s look and feel is Jilion’s SublimeVideo, an HTML5 video playback system that ensures your video has the same controller across browsers. It bypasses the browser’s built-in controller in favor of Jilion’s (very slick) custom controller. Jilion’s blog states (emphasis mine):

We aim at delivering a modern and simple video-embedding solution for web developers that provides the same user experience and uniform UI across browsers.

Jilion’s work comes from the same school of thought that led designers/developers to change the look and feel of other built-in browser components such as radio buttons and checkboxes. (Guilty!)

While looking at Jilion’s fine handiwork, I was struck by a thought: If a person uses a particular browser regularly, they will be accustomed to that browser’s native controls — whether it’s for video or drop-down menus in forms — and might be thrown off by the custom controls. Maybe it isn’t such a good idea to create a homogeneous video controller for all browsers.

After all, why does a Firefox user need to have the same experience as a Chrome user? Why does an Opera user need to have the same experience as an Internet Explorer or Safari user? These people have probably never seen what a webpage looks like in a different browser, anyway. What benefit do site visitors really receive from “a uniform UI across browsers”?

If you change the default controls to match the look and feel of something your visitor has never seen before, you run the risk of creating confusion, distrust, or alienation. Even worse, if the controls are poorly made or conceived — and many are — you might make your site less usable. A cardinal sin.

The more I think about it, the real beneficiaries of a uniform UI across browsers aren’t the site visitors, but rather the designers who demand artistic control and the clients who insist the product looks the same everywhere, without understanding that it’s okay (even expected) to have some differences.

Personally, I realized 100% uniformity is an unnecessary hardship when I began adopting HTML5 and CSS3 features into pipwerks.com last year. These new features are still not supported in all browsers, and essentially forced me to give up IE6 support. (pipwerks.com uses Andy Clarke’s handy Universal IE6 stylesheet for IE6.)

I’ve come to grips with the notion that my site’s design won’t look the same in Opera 9 or IE7/8 because those browsers don’t support border-radius. My design is simple enough that the general impression is preserved across browsers, but people who use certain browsers will see a slightly less refined product. This is a-ok with me.

It should also be noted that growth in the mobile market appears to be shifting perceptions about consistency between browsers, too. Most major sites have a “mobile edition” which will look quite different than the standard website, and is most likely geared to look great on an iPhone and so-so in all other mobile devices.

Rounded corners on images using CSS3

Most browsers do not allow images to be cropped using CSS3’s border-radius. Tim Van Damme recently posted a workaround for this issue. It’s a nice trick, and doesn’t require JavaScript.

It does, however, require an extra span to be added to the page’s markup, which is quite a pain to manually apply to each image. Bram Van Damme (no relation to Tim) posted a simple jQuery script that automate’s Tim’s workaround with minimal effort. It’s a perfect example of using JavaScript for progressive enhancement purposes, as the page is still usable when JavaScript and/or CSS is disabled.

Being a MooTools kind of guy, I decided to whip up a MooTools-flavored version. Enjoy!


window.addEvent("domready", function (){
    $$("img").each(function(img){
        new Element("span", {
            "class": "rounded",
            styles: {
                "background-image": "url(" + img.getProperty('src') + ")",
                height: img.getProperty('height') + "px",
                width: img.getProperty('width') + "px"
            }
        }).wraps(img);
    });
});

Bear in mind this JavaScript relies on your page having some pre-defined CSS:


.rounded {
    -webkit-border-radius: 25px;
    -moz-border-radius: 25px;
    border-radius: 25px;
    display: block;
}
.rounded img { opacity: 0; }

View demo

Notes:

  • The demo page has the code wrapped in a function, which makes the code reusable; it accepts any CSS selector, such as “img”, “#myid img”, “p img”, “img.rounded”, etc.
  • This will only work in browsers that support CSS3 border-radius, which means no IE6/7/8 or older versions of Safari, Firefox, or Opera.

HTML5 Video, minus Ogg

As you’ve probably read somewhere on the interwebs, HTML5 is bringing native video support to browsers. This will enable us to embed a video file directly in our HTML, much like a SWF or image.

Background

You may have also heard that there’s currently a big controversy over what kind of video files will be supported. The defacto standard is MP4/H.264, which is used by Adobe in their Flash video format, and by huge media sites like YouTube. Mozilla, the makers of Firefox, refuse to support the MP4/H.264 standard because it isn’t open-source and free from licensing constraints.

Turns out H.264 is not public domain. Although the company that owns the H.264 patent has temporarily agreed to waive royalty fees for the next decade or so, they reserve the right to charge fees later on. Mozilla says no way, we will only support a video format that is free from licensing issues and has no patent holders (because patent holders can decide to sue some day). Mozilla supports the completely free/open-source Ogg format.

Apple and Adobe, already knee-deep in MP4/H.264 with their Quicktime and Flash video products, vow to press on with H.264.  Google also supports H.264 because YouTube relies on it, and because Google’s new Chrome browser is based on the WebKit project, which has Apple as a main code contributor. In case you haven’t noticed, Apple, Adobe and Google have pretty much cornered the internet video market the past few years, so if they’re throwing their support behind H.264, you can count on it being around for a while. Not to mention that many mobile devices, including the iPhone and most Android phones, have hardware that is designed specifically to support H.264 video, enabling smoother playback and less battery drain than non-dedicated hardware.

(For what it’s worth, Opera is in agreement with Mozilla and supports Ogg. However, not many people seem to pay attention to Opera these days, so they don’t appear to have much influence in this race. Microsoft has endorsed H.264 with it’s upcoming IE9 browser, but it won’t be available for some time.)

The problem

Firefox and Opera are essentially forcing websites to offer two versions of each video: an Ogg version and an MP4 version. In my opinion — and the opinion of many others — this simply will not do. Providing two different video files is not realistic, Ogg’s quality is inferior to H.264, and many computers and mobile devices have direct hardware support for H.264 but not Ogg. In reality, without MP4 support, HTML5 video is rendered useless for most site developers in Firefox and Opera.

The most logical workaround is to code <video> elements to work for MP4 and have a Flash Player-based fallback for older browsers and browsers that only support Ogg. Since the <video> element is designed to allow for fallback content, just like the <object> element, we can do this:


<video id="myvideo" width="480" height="360" controls>
    <source src="/video/file.m4v" type="video/mp4"></source>
    <object data="flash-video-player.swf" type="application/x-shockwave-flash" width="480" height="360">  
        <param value="flash-video-player.swf" name="movie"/>  
        <param value="file=/video/file.m4v" name="flashvars"/>
    </object>
</video>

This works fine in Safari, Chrome, Internet Explorer and older versions of Firefox and Opera that don’t support the <video> element. However, Firefox 3.6 and Opera 10.5 do something very irritating: even though they KNOW their <video> doesn’t support “video/MP4”, they load the <video> element anyway. Which is … like … OMG so duh … because the video can’t possibly play!

If the <video> element is loaded, Firefox and Opera will never load the fallback <object> containing the Flash-based fallback.

Because this behavior cannot be fixed with markup, we’re forced find a scripted workaround (notice that we haven’t used a single bit of JavaScript yet). Thankfully, there’s a pretty straightforward solution: Delete the <video> element in Firefox and Opera.

A Solution

Here’s a simple script that will detect whether HTML 5 video is supported, and if it is, will check to see if MP4 is supported. If HTML5 video is supported but MP4 is NOT supported, the script deletes the specified <video> element but leaves the Flash fallback in its place.


var detectVideoSupport = function (){
    var detect = document.createElement('video') || false;
    this.html5 = detect && typeof detect.canPlayType !== "undefined";
    this.mp4 = this.html5 && (detect.canPlayType("video/mp4") === "maybe" || detect.canPlayType("video/mp4") === "probably");
    this.ogg = this.html5 && (detect.canPlayType("video/ogg") === "maybe" || detect.canPlayType("video/ogg") === "probably");
    return this;
};

var replaceVideoWithObject = function (video_id){    
    if(!video_id){ return false; }
    var video = document.getElementById(video_id);
    if(video){
        var obj = video.getElementsByTagName("object")[0];
        if(obj){
            var obj_copy = obj.cloneNode(true);
            video.parentNode.insertBefore(obj_copy, video);
            video.parentNode.removeChild(video);
        }
    }
};

window.onload = function (){
    var video = detectVideoSupport();
    //Both Opera and Firefox support OGG but lack MP4 support
    if(video.ogg && !video.mp4){
        replaceVideoWithObject("myvideo");
    }
};
</script>

Functioning demo.

A few notes

Tested successfully in:

  • Windows XP: Firefox 3.0, Firefox 3.5.8, Internet Explorer 6, Internet Explorer 8, Google Chrome 4.1.2
  • Windows Vista: Internet Explorer 7
  • Mac OS X (10.6): Firefox 3.6, Safari 4.01, Chrome 5 (beta), Opera 10.1, Opera 10.5b

(Note: IE6, IE7 & IE8 give an unexplained “object required” error in the demo page, but everything works fine. I will investigate as time permits.)

This demo uses the JW Media Player as the Flash fallback video player, but you can use any Flash-based video player on your page.

This demo doesn’t do any Flash Player version detection to ensure the visitor has a supported version of Flash Player. If you need to add Flash Player version detection, you can use SWFObject to embed your Flash file.

Update April 3, 2010: This post was updated to add Opera 10.5 to the list of non-behaving browsers and remove Firefox user agent sniffing.

A new removeClasses utility for MooTools

Note: If you want to skip to the final code (in both MooTools and framework-neutral flavors), it’s at the bottom of this post.

The problem

Readers of this blog know that I enjoy MooTools. Like other JavaScript frameworks, it has many excellent features, including addClass and removeClass functions, which I use all the time. However, when I was working on my CustomInput class the other day, I discovered a major shortcoming of MooTools’ removeClass function — it doesn’t work very well when trying to remove multiple classes (as of MooTools version 1.2.4). In particular, if you specify multiple classes to remove, removeClass will only work if the classes are listed in that element’s className property in the order specified.


//Assuming element has className "hello cruel world"

//Single terms work fine
element.removeClass("world"); //becomes "hello  world"
element.removeClass("hello"); //becomes " cruel world"

//Multiple terms listed in same order as className works fine
element.removeClass("hello cruel"); //becomes " world"

//Multiple terms NOT listed in same order as className fail
element.removeClass("hello world"); //remains "hello cruel world"
element.removeClass("cruel hello"); //remains "hello cruel world"

The cause

A peek at MooTools’ removeClass code reveals the shortcoming:


removeClass: function(className){
   this.className = this.className.replace(new RegExp('(^|\s)' + className + '(?:\s|$)'), '$1');
   return this;
}

The string containing the class names you want to remove is passed as-is; it remains a whole string, and is not broken down into substrings. So "hello world" remains the single string "hello world" instead of two separate strings "hello" and "world".

I tested jQuery’s removeClass function and noticed it doesn’t have the same problem; it will remove each word, no matter what order you specify. Taking a look under the hood reveals a completely different approach to removeClass:


removeClass: function( value ) {
   if ( jQuery.isFunction(value) ) {
      return this.each(function(i) {
         var self = jQuery(this);
         self.removeClass( value.call(this, i, self.attr("class")) );
      });
   }

   if ( (value && typeof value === "string") || value === undefined ) {
      var classNames = (value || "").split(rspace);

      for ( var i = 0, l = this.length; i < l; i++ ) {
         var elem = this[i];

         if ( elem.nodeType === 1 && elem.className ) {
            if ( value ) {
               var className = (" " + elem.className + " ").replace(rclass, " ");
               for ( var c = 0, cl = classNames.length; c < cl; c++ ) {
                  className = className.replace(" " + classNames[c] + " ", " ");
               }
               elem.className = jQuery.trim( className );

            } else {
               elem.className = "";
            }
         }
      }
   }

   return this;
}

The big difference between jQuery and MooTools in this case is that jQuery converts the arguments to an array (split using spaces as a delimiter) then loops through the className property to search for each word in the array, whereas MooTools performs a simple full-string replace using regular expressions.

The search for a solution

My first reaction was to build a MooTools-flavored variation of jQuery’s removeClass code.


Element.implement({
   removeClasses: function (classNames) {
      if(this.className){
         var classNameString = this.className;
         classNames.split(/s+/).each(function (term){
            classNameString = classNameString.replace(term, " ");
         });
         this.className = classNameString.clean();
      }
      return this;
   }
});

It follows the same basic principles of the jQuery version, but uses MooTools’ Array.each and String.clean utility functions. It works well, but I wasn’t thrilled about using a loop. I thought maybe a regular expression would be better suited for the job.

I eventually came up with this:
(purposely verbose to explain what’s happening)


Element.implement({
   removeClasses: function (classNames) {
      if(classNames && this.className){
         //Replace all spaces in classNames with vertical beams
         var terms = classNames.replace(/s+/g, "|");
         //Create a regular expression using terms variable
         var reg = new RegExp('\b(' + terms + ')\b', 'g');
         //Use the new regular expression to replace all specified terms with a space
         var newClass = this.className.replace(reg, " ");
         //Use MooTools' 'clean' method to remove extraneous spaces
         newClass = newClass.clean();
         //Set element's classname to new cleaned list of classes
         this.className = newClass;
      }
      return this;
   }
});
Notes:

  • I’m no expert on JavaScript speed tests, so for all I know a loop might be quicker. However, a regular expression feels more elegant.
  • I named my utility removesClasses so it doesn’t overwrite MooTools’ built-in utility.

Final MooTools version

Here’s a more concise version:


Element.implement({
   removeClasses: function (classNames) {
      this.className = this.className.replace(new RegExp("\b(" + classNames.replace(/s+/g, "|") + ")\b", "g"), " ").clean();
      return this;
   }
});

You can see it in action via the MooShell (apparently the MooTools Shell is no longer online)

Standalone (framework-neutral) version

For those of you who don’t use MooTools, a few small edits will allow you to use this code without relying on any outside JavaScript libraries. First, a verbose version explaining what’s happening:


function removeClass(el, classNames) {
   //Only run if the element is available and supports the className property
   if(el && el.className && classNames){
         //Replace all spaces in classNames with vertical beams
         var terms = classNames.replace(/s+/g, "|");
         //Create a regular expression using terms variable
         var reg = new RegExp('\b(' + terms + ')\b', 'g');
         //Use the new regular expression to replace all specified terms with a space
         var newClass = el.className.replace(reg, " ");
         //Use regular expression to remove extraneous whitespace between class names
         newClass = newClass.replace(/s+/g, " ");
         //Use regular expression to remove all whitespace at front and end of string
         newClass = newClass.replace(/^s+|s+$/g, "");
         //Set element's classname to new cleaned list of classes
         el.className = newClass;
   }
}

The concise version:


function removeClass(el, classNames) {
   if(el && el.className && classNames){
      el.className = el.className.replace(new RegExp("\b(" + classNames.replace(/s+/g, "|") + ")\b", "g"), " ").replace(/s+/g, " ").replace(/^s+|s+$/g, "");
   }
}

Used as follows:


var myelement = document.getElementById("myelement");
removeClass(myelement, "one three");
//<div id="myelement" class="one two three"></div>
//becomes
//<div id="myelement" class="two"></div>

Enjoy!

Successfully tested in Internet Explorer 6 (WinXP), Firefox 3.5 (WinXP), Firefox 3.6 (OS X 10.6.2), Safari 4 (OS X 10.6.2), Opera 10.1 (OS X 10.6.2), Chrome 5 (OS X 10.6.2)

CustomInput Class: Accessible, Custom-Styled Checkboxes and Radio Buttons

I’m a big fan of the Filament Group’s UI work.  They put a lot of thought into their work, and ensure everything they make is not only beautiful, but as accessible and as semantic as possible.

One of my favorite pieces of work by the Filament Group is their approach to stylized checkbox and radio <input> elements, as described in their post Accessible, Custom Designed Checkbox and Radio Button Inputs Styled with CSS (and a dash of jQuery)

The system is remarkably well-constructed, degrades gracefully, and is completely accessible if JavaScript, image loading, or CSS is disabled can’t be loaded. No small feat. Plus it remains semantically sound, with no bloated markup.

Having said all that, I’ve never actually used any of Filment Group’s UI code in my projects because of one little obstacle: they rely on jQuery.  Mind you, I’m not a jQuery hater, but I prefer MooTools and build all my major projects using MooTools. Switching to jQuery for such a small UI customization is not going to happen. I’ve often thought it would be great to build a MooTools version of Filament’s UI examples, but never had the time… until today.

I’m currently working on a new quiz system at work, and decided I’d incorporate Filament’s wonderful stylized checkboxes and radio buttons into my project, which meant it was time to roll up my sleeves and code me some Moo. 

View Demo

I made a couple of changes to the underlying JavaScript to suit a MooTools approach (and add some flexibility with the CSS selectors) but the basic premise is the same as Filament’s demo.

The following JavaScript will style ALL checkbox and radio inputs on your page. Note that CustomInput returns the collection of elements that have been styled.


window.addEvent("domready", function(){ 
    var all_styled_inputs = new CustomInput();
});

If you want to target specific parts of the page or only certain input types, pass a CSS selector as an argument:


window.addEvent("domready", function(){
    //Only style checkboxes
    var styled_checkboxes = new CustomInput("input[type='checkbox']");

    //Only style radios in a div named "walkman"
    var styled_radios = new CustomInput("#walkman input[type='radio']");
});

The CustomInput class has been fully JSLinted, and remains very close to the original jQuery version’s size. When compressed (YUI), it squeezes down to about 1.6kb. It uses ‘dollar-safe’ mode for compatibility with other JS libraries.

It has been successfully tested in the following systems:

Windows XP

  • Internet Explorer 6
  • Firefox 3.5
  • Safari 4
  • Chrome 4
  • Opera 10

Mac OS X (10.6.2)

  • Firefox 3.6
  • Safari 4
  • Opera 10.1
  • Chrome 5 (beta)

View Demo | Download Project Files

Major kudos to Filament Group for sharing their ideas with the world.

Update 3/12/2010: Fixed a typo in the JS file preventing the checkedHover class from being assigned.

Introducing LearnSWFObject.com

I’m happy to introduce you to my latest project, LearnSWFObject.com

It’s been many, many months in the making, and has rudely been put aside a number of times when my life left me no free time to work on it. Happily, I’ve been able to push through the last remaining barriers and get the site out the door. Hopefully someone will find it useful.

I’ve ported my most popular SWFObject tutorials and examples to the new site — updated, of course — and have added a few new tutorials, too. As part of the housecleaning effort, I’ve removed all SWFObject examples and tutorials from pipwerks.com. 301 redirects will re-route the most popular posts to their LearnSWFObject.com equivalent, but the odds and ends have been sent to the compost heap.

I’m most excited about the brand-new code generator (written from scratch) that will write your SWFObject embed code for you. Some notes about the generator:

  • It can write HTML 5, HTML 4 (transitional/strict), and XHTML 1 (transitional/strict) doctypes
  • It provides code for both types of SWFObject embeds: dynamic (JavaScript) and static (markup)
  • It includes a download link that lets you save your generated markup in an HTML file
  • The static publishing option includes an option for a nice, shorter syntax
  • The generator uses progressive enhancement techniques, meaning it’s nicer with JavaScript enabled but fully functional without it

Give it a try!

LearnSWFObject.com will remain a work in progress, as SWFObject itself continues to evolve and people find news ways to break use it in their sites. I will continue to play with the formatting and layout from time to time.

In other SWFObject news, I think I’m allowed to report that SWFObject 2.3 is in the works, and will contain mostly bug fixes and an enhancement or two. Bug reports and feature requests are always welcome at SWFObject’s Google Code site. If you have questions about how to use SWFObject that aren’t answered by the LearnSWFObject.com tutorials, please post them on the SWFObject Google Group.

Fear of sharing, fear of failing

Janet Clarey posted a link to a great blog post by Rajesh Setty entitled Why some smart people are reluctant to share? Setty’s insights resound with me, not because I think I’m smart — quite the opposite, actually — but because the more I learn, the more I’m aware of my limitations.

Setty tried to determine why “smart people” are often reluctant to share their knowledge with others. His conclusion was:

Smart people want to give their best and as they learn more, they learn that they need to learn a lot more before they start sharing. They learn some more and they learn they need to learn some more. What they forget is that most of the expertise that they already have is either becoming “obvious” to them or better yet, going into their “background thinking.”

I agree with the points in Setty’s post completely, except in I’d substitute the word “experienced” for “smart.”

Setty’s line “Smart people want to give their best” seems to be something of a passing thought in his post. I’d like to give it more attention, because I believe conscientious experienced folks have a fear of giving bad advice.

Zeldman had it right when he said “If your old work doesn’t shame you, you’re not growing.” Experienced people look back at their younger, inexperienced self and either chuckle at their own gumption or get rosy-cheeked from embarrassment. I’m more of the rosy-cheeked guy. My old work shames me all the time, and it often causes me to hesitate when sharing my work or giving advice to others.

This may sound funny to some of you… anyone who follows this blog probably knows I spend a lot of time doling out technical advice via places like the SWFObject and eLearning Technology and Development Google Groups.  The truth is, I question myself in almost each and every post I write. Why? Because I’m experienced enough to know that maybe I shouldn’t be so quick and cocky with an answer. Maybe there’s a different solution I haven’t heard of. To paraphrase Setty, maybe I need to learn more first.

So why do I even try to give advice if I have a fear of giving bad advice? Because I want to learn more. I often find that helping others is the best way to teach myself. I’ve learned a ton through helping others, and find it rewarding in many ways. Except when I’m wrong, and then it plain sucks.