TextAreaExpander Class for MooTools

I’ve had to create a slew of forms over the last few months, and have become fond of textareas that auto-expand when they become filled. There are a number of MooTools scripts on the interwebs that can handle this task, but none of them suited me, and I generally like to roll my own code as a learning experience.

So, without further ado, I present a very simple MooTools Class: TextAreaExpander. It does exactly what is says: expand textareas. No more, no less.

Details

Options

Defaults are shown.

  • textarea_selector: none
    • Uses CSS selector syntax, such as “#myId” or “textarea.expand-o-rama”
  • padding_bottom: 0
    • Only accepts pixels, no percentages or ems
  • fx_transition: Fx.Transitions.Expo.easeOut
  • fx_duration: 350

Returns

Array of textarea elements

Requires

MooTools 1.2.4 core

Demos

Sample implementation using defaults (grabs ALL textareas on the page):


window.addEvent("domready", function (){
    new TextAreaExpander();								  
});

View ‘simple’ demo

 

Sample implementation specifying all available parameters:


window.addEvent("domready", function (){
    new TextAreaExpander({
        textarea_selector: "#demo",
        padding_bottom: 12,
        fx_transition: Fx.Transitions.Quad.easeInOut,
        fx_duration: 500
    });
});

View ‘options’ demo

Download

License

MIT-style license. Completely free. Enjoy.

Advertisements

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.

What feature is missing from your e-learning development tool?

I have some simple questions I’d love to get feedback on.

I’m curious about what people are looking for in their e-learning authoring tools, specifically:

  1. What feature is your current tool missing that you would love to see implemented? Support for team collaboration? Support for themes or custom CSS styling? Support for language localization? A beer dispenser? Etc.
  2. What feature does someone else’s tool have that you’re jealous of?

For me? I wish the most popular tools (Captivate, Articulate, Lectora, etc.) would output cleaner HTML and JavaScript. I also wish there was less reliance on Flash and PowerPoint. But if you read my blog regularly, you probably already knew that!

Please post your opinion in the comments below, and please ask others to give their 2 cents. (You can also reply via twitter if you prefer)

I’m really looking forward to your comments. Thanks!

Changes to pipwerks.com, part 2

In case you hadn’t heard, pipwerks.com was hacked last week. The entire database was erased. Bastages. Luckily, I had a recent backup.

While going through the pains of a new WordPress install (with new plugins, extra security, and imported posts/comments), I decided “why not throw a new layout in the mix, too?” I mean, if I’m going to make changes, I may as well do them all in one shot, eh?

I had been working on an HTML 5-based layout for some time, but had been reluctant to publish it because of HTML 5’s newness and uncertainty; where do I use ‘section’ versus ‘article’, etc.? Do I really want to let WordPress continue to abuse <ol> and <ul>, or does it make more sense to use <dl> in some cases?

The outcome of the argument is what you see today. I didn’t want to fight WordPress too much (esp. the plugins), so there are still a bunch of legacy <div>, <ul> and <ol> elements hanging around. I’m sure I’ve neglected a number of things, but for the most part I’m satisfied.

Here is a short list of fun stuff i implemented in this theme:

  • <dl> for comments (<dt> for author, <dd> for comment body). I feel this is more appropriate as it maintains a relationship between the comment author and the comment body. It also allows for interesting styling (though it makes threaded comments more difficult).
  • <nav> for page header
  • <section> to demarcate where the sidebars go, “related posts” lists, etc.
  • <article> to contain the post itself
  • <footer> to hold footer-ish stuff (though the definition of footer in HTML 5 is still a work in progress)
  • Widgets for sidebars and the footer, which make editing the template SO much easier!
  • Myriad Pro as the body font. Yes, many people won’t have it, and they’ll see Arial instead. But many of my readers probably have Adobe products, which include Myriad. So there you go. No @font-face required! ๐Ÿ˜›
  • Malarkey‘s Universal IE 6 CSS for the laggards who still use IE 6. No offense — we still have IE 6 at the office, too — but I simply am not interested in bending over backwards to support IE 6 anymore. Kthxbai, IE 6!
  • Alpha transparency in images to allow overlays (such as the cartoon head). Easy in every browser except IE 6 (see above)
  • CSS-based rounded corners in browsers that support them (Safari and Firefox at the moment)
  • The comments styling uses a CSS trick to create the triangle that gives the impression of a cartoon ‘talk’ bubble
  • Icons for external links via CSS3
  • Meyer’s CSS Reset
  • Gravatars
  • Sociable links
  • Related Posts displayed for each post
  • …and more!

I must say I really enjoyed working on this theme, though it took me way too long to get to this point. Please have a look around and let me know how you like the site (or, heaven forbid, you find any problems/errors). And of course, a big thank you to everyone who contributed to the CSS and plugins that I’m using! Your hard work makes my life easier and I appreciate it.

Fixed-width layouts

While working on a recent web project at work, I wondered if I should go for a fixed-width layout or stick with my preference for fluid layouts. Fixed-width layouts are certainly easier to manage, but they just feel so… rigid. With the boom in larger monitors, I also wondered if fluid sites start presenting a problem due to being too wide. I decided to check around the web to see what others are doing.

The search

In my (very quick and unscientific) research, I visited 150 popular sites, including news sites, shopping sites, software company sites, and the personal sites of well-regarded web designers and developers. I purposely avoided blogs that use canned themes.

When visiting the site, I determined the page’s width by reducing the browser window’s width until the horizontal scrollbar appeared. In some cases, I didn’t use the front page of the site, as these are sometimes standalone pages that use a completely different layout than the site’s actual content.

The results

Of the 150 sites I visited, only thirteen used fluid layouts — a whopping 8.6%.

A number of those thirteen sites weren’t completely fluid, as they either break at smaller sizes or have fixed-width sidebars, but I included them in the list since they aren’t using a fixed-width wrapper.

I was genuinely surprised about the number of fixed-width sites; a sizable chunk of these sites belong to (or were designed by) well-known web design gurus. I had assumed a number of them would use some sort of min-width/max-width CSS trickery, but I was wrong — only a very few sites (less than 3.5%) used this approach.

I was also surprised to see that the vast majority of the fixed-width sites were between 900 and 1000 pixels wide, a size that was unthinkable a mere 5 years ago.

Observations

  • Most of the fixed-width sites fell between 900 and 1000 pixels wide, and were usually centered in the browser
  • 20 sites were 800 pixels wide or less
  • 14 sites were between 800 and 900 pixels wide
  • 100 sites were between 900 and 1050 pixels wide
  • 3 sites were over 1050 pixels wide
  • the widest site was time.com at over 1100 pixels, although the layout uses a fixed-width wrapper wider than the content itself (the content was closer to 1000 pixels).

Sites visited

I picked sites somewhat randomly, using a combination of Alexa.com rankings, sites I frequent, sites my co-workers like to visit (hello, www.bebe.com), sites belonging to famous companies (McDonalds, Target, etc.) and sites belonging to well-known web designers and developers. I know I left out a number of good sites, but this was a very quick-and-dirty project.

Fluid layouts

  1. allinthehead.com (Drew McClellan)
  2. clearleft.com
  3. craigslist.org
  4. danwebb.net
  5. dean.edwards.name
  6. drupal.org (partially broken due to floating elements overlaying other elements)
  7. gmail.com
  8. htmldog.com (Patrick Griffiths)
  9. joeclark.org
  10. meyerweb.com (Eric Meyer)
  11. molly.com (Molly Holzschlag)
  12. people.opera.com/howcome (Hรƒยฅkon Wium Lie)
  13. wikipedia.org

Fixed-width layouts

  1. 24ways.org
  2. 37signals.com
  3. 456bereastreet.com (Roger Johansson)
  4. about.com
  5. adactio.com (Jeremy Keith)
  6. adobe.com
  7. alexa.com
  8. alistapart.com
  9. amazon.com
  10. americanexpress.com
  11. andybudd.com
  12. anthropologie.com
  13. aol.com
  14. apartmenttherapy.com
  15. apple.com
  16. authenticjobs.com
  17. bankofamerica.com
  18. barackobama.com
  19. barnesandnoble.com
  20. bbc.co.uk
  21. bebe.com
  22. bestbuy.com
  23. blogger.com
  24. borders.com
  25. boxofchocolates.ca (Derek Featherstone)
  26. burgerking.com
  27. cameronmoll.com
  28. cartoonnetwork.com
  29. cbs.com
  30. chase.com
  31. clagnut.com (Richard Rutter)
  32. cnet.com
  33. cnn.com
  34. comcast.com
  35. comcast.net
  36. crateandbarrel.com
  37. danbenjamin.com
  38. dean.edwards.name
  39. dell.com
  40. deviantart.com
  41. dictionary.com
  42. digg.com
  43. directv.com
  44. dojotoolkit.com
  45. dustindiaz.com
  46. ebay.com
  47. espn.com (MLB page)
  48. facebook.com
  49. fastcompany.com
  50. fedex.com
  51. flickr.com
  52. fox.com
  53. friendster.com
  54. gamespot.com
  55. go.com
  56. guardian.co.uk
  57. guitarhero.com
  58. happycog.com
  59. haveamint.com
  60. hicksdesign.co.uk
  61. home.live.com
  62. hulu.com
  63. iht.com
  64. ikea.com
  65. imdb.org
  66. jasonsantamaria.com
  67. jeffcroft.com
  68. jetblue.com
  69. jquery.com
  70. kaiserpermanente.com
  71. latimes.com
  72. linkedin.com
  73. livejournal.com
  74. m-w.com
  75. macys.com
  76. markboulton.com
  77. mcdonalds.com
  78. mediatemple.net
  79. mezzoblue.com (Dave Shea)
  80. microsoft.com
  81. mootools.net
  82. mozilla.com
  83. msn.com
  84. myspace.com
  85. nbc.com
  86. neopets.com
  87. netflix.com
  88. newegg.com
  89. nfl.com
  90. ning.com
  91. nintendo.com
  92. npr.org
  93. nytimes.com
  94. opera.com
  95. oreilly.com
  96. paypal.com
  97. pbs.org
  98. quirksmode.com (Peter-Paul Koch)
  99. reuters.com
  100. rockband.com
  101. rollingstone.com
  102. secondlife.com
  103. sfgate.com
  104. shauninman.com
  105. si.com (MLB page)
  106. simonwillison.net
  107. simplebits.com (Dan Cederholm)
  108. sitepoint.com
  109. skype.com
  110. snook.ca (Jonathan Snook)
  111. sony.com
  112. stuffandnonsense.co.uk (Andy Clarke)
  113. target.com
  114. techcrunch.com
  115. theonion.com
  116. time.com
  117. tivo.com
  118. twitter.com
  119. typepad.com
  120. ups.com
  121. urbanoutfitters.com
  122. usaa.com
  123. usps.com
  124. veerle.duoh.com (Veerle Pieters)
  125. virgin.com
  126. wait-til-i.com (Christian Heilmann)
  127. wamu.com
  128. washingtonpost.com
  129. weather.com
  130. wellsfargo.com
  131. whitehouse.gov
  132. williamssonoma.com
  133. wordpress.com
  134. yahoo.com
  135. yelp.com
  136. youtube.com
  137. zeldman.com (Jeffrey Zeldman)

Adding SCORM code to an HTML file using the pipwerks SCORM wrapper

In my previous post, I briefly explained how to add SCORM code to an existing Flash file by using the pipwerks SCORM wrapper and SCORM ActionScript class.

Today, I’m going to explain how to add SCORM code to a plain HTML file. This example uses SCORM 1.2 syntax, but as I explain at the end of the tutorial, it’s really easy to edit the code to use SCORM 2004 syntax.

I certify that…

Let’s say you have a project where all you need to do is allow a user to confirm that they have completed a task, sort of like a digital signature. In e-learning, our clients often run into compliance issues where all they really need to do (or have time to do) is prove an employee was exposed to certain training materials.

For example:

“I, [name here], certify that I have read the materials presented to me.”
[Button: I agree]

This scenario is really easy to handle in any LMS with SCORM support. Let’s start by creating an HTML file containing a simple form.

Create the HTML

I’m using XHTML 1.0 Transitional in this example, but you can use XHTML 1.0 Strict or HTML 4 if you prefer… it doesn’t really matter.



<form id="myform" method="post">
  <fieldset>
  <legend>Please indicate your choice</legend>

I, [name here], certify that I have read the materials presented to me.
<input id="submit" name="submit" type="submit" value="Yes, I agree" />

  </fieldset>
  </form>

Here is a functioning example of the plain HTML file (with CSS styling added).

Add some JavaScript to handle the form submission

JavaScript will be used to handle the form submission, SCORM communication, and text edits/feedback messages. Let’s start by adding a handler for the form submission.

Note: I always try to use progressive enhancement techniques in my web projects — avoid all inline scripting — which means we’ll use a window.onload event to apply our form submission code:


<script type="text/javascript">
window.onload = function (){
  //do something
};
</script>

In our simple example, we want to provide feedback when the form is submitted (something along the lines of “Your choice has been recorded”). We also want to disable the submit button when it’s clicked so that it doesn’t get submitted twice by mistake. We can kill two birds with one stone here by replacing the submission form with a custom feedback message. And it only takes one line of code in the initForm function! Sweet!


function initForm(){
    document.getElementById("myform").onsubmit = function (){
    this.innerHTML = "Thank you, your selection has been recorded. You may close this window.";
    return false; // This prevents the browser from trying to post the form results
  }
}

Here’s what we have so far: functioning example of the plain HTML file with JavaScript handling the form submission (CSS styling added).

SCORM it up

Okay, now that we have a functioning HTML file, lets’ add some SCORM code to it! The ‘course’ will need to perform the following tasks:

  1. Connect to the LMS
  2. Get the learner’s name (and add it to the HTML page)
  3. Check for a previous completion
  4. Set the course to “completed” if the user clicks the form submission button
  5. disconnect from the LMS

Before we can start adding the SCORM code, we need to link to the pipwerks SCORM API wrapper (this example uses version 1.1.5).

Add the SCORM wrapper

Download the SCORM API wrapper, then add it to your page before the other JavaScript code.


<script src="SCORM_API_wrapper.js" type="text/javascript"></script>

Note: I’ve seen that some people don’t understand the difference between my pipwerks SCORM wrapper and the SCORM wrapper available from the ADL or other sources. The short version is: you only need the pipwerks wrapper! Do not include the other wrappers or you may run into errors; the pipwerks wrapper was designed to replace those wrappers while adding extra functionality and error-checking. As MacLeod used to say, “There can be only one…”. ๐Ÿ˜‰

Add the SCORM code to your JavaScript

After linking to the wrapper, you’ll need to create two global variables (you can use object notation if you prefer, but I’ll keep it simple here to make it easier to follow).

var scorm = pipwerks.scorm;
var lmsConnected = false;

The variable scorm is a shortcut that helps reduce typing. If you didn’t use this shortcut, you’d need would need to use the full phrase <strong>pipwerks.scorm</strong>.xxx wherever you see <strong>scorm</strong>.xxx.

The variable lmsConnected is used to store a boolean (true/false) indicating whether or not we’re connected to the LMS.

Error handling

You should always include some kind of error handling in your code to let the user know what’s going on. For this example, I’m using a function that displays an alert, then closes the course window.

function handleError(msg){
   alert(msg);
   window.close();
}

Starting the course

Before we can make any calls to the LMS, we have to get the course connected! We’ll do this using a function named initCourse (feel free to use another name if you like).


function initCourse(){

   //scorm.init returns a boolean
   lmsConnected = scorm.init();

   //If the scorm.init function succeeded...
   if(lmsConnected){

      //Let's get the completion status to see if the course has already been completed
      var completionstatus = scorm.get("cmi.core.lesson_status");

      //If the course has already been completed...
      if(completionstatus == "completed" || completionstatus == "passed"){

         //...let's display a message and close the browser window
         handleError("You have already completed this course. You do not need to continue.");

      }

      //Now let's get the username from the LMS
      var learnername = scorm.get("cmi.core.student_name");

      //If the name was successfully retrieved...
      if(learnername){  

         //...let's display the username in a page element named "learnername"
         document.getElementById("learnername").innerHTML = learnername; //use the name in the form

      }

   //If the course couldn't connect to the LMS for some reason...
   } else {

      //... let's alert the user then close the window.
      handleError("Error: Course could not connect with the LMS");

   }

}

Edit the HTML

If you read through the code comments, you’ll see that we need to add an element named “learnername” to the page. I plan to display the username in the main sentence; here’s the edited HTML code:

<form id="myform" method="post">
  <fieldset>
  <legend>Please indicate your choice</legend>

I, <span id="learnername">[name here]</span>, certify that I have read the materials presented to me.
<input id="submit" name="submit" type="submit" value="Yes, I agree" />

  </fieldset>
</form>

The function initCourse will replace the text [name here] with the cmi.core.student_name from the LMS.

Add the completion code

Now that we’ve connected to the LMS, how do we set the course to complete? With a function named setComplete, of course! (Again, you can rename any of these functions of you prefer)

function setComplete(){

   //If the lmsConnection is active...
   if(lmsConnected){

      //... try setting the course status to "completed"
      var success = scorm.set("cmi.core.lesson_status", "completed");

      //If the course was successfully set to "completed"...
      if(success){

         //... disconnect from the LMS, we don't need to do anything else.
         scorm.quit();

      //If the course couldn't be set to completed for some reason...
      } else {

         //alert the user and close the course window
         handleError("Error: Course could not be set to complete!");

      }

   //If the course isn't connected to the LMS for some reason...
   } else {

      //alert the user and close the course window
      handleError("Error: Course is not connected to the LMS");

   }

}

Finishing touches

All that’s left now is adding the setComplete(); call to the form submission event (the course will not be set to complete unless the form is submitted), and adding the initCourse() call to the window.onload event:

function initForm(){

  document.getElementById("myform").onsubmit = function (){

    this.innerHTML = "Thank you, your selection has been recorded. You may close this window.";

    setComplete();

    return false; // This prevents the browser from trying to post the form results

  }

}

window.onload = function (){

  initCourse();
  initForm();

}

Here is the final product.

Prefer SCORM 2004 over SCORM 1.2?

This example uses the SCORM 1.2 calls cmi.core.lesson_status and cmi.core.student_name. If you prefer to use SCORM 2004, just change cmi.core.lesson_status to cmi.completion_status and cmi.core.student_name to cmi.learner_name. You should also remove the code snippet || completionstatus == "passed", since SCORM 2004 doesn’t use the term “passed”. The rest of the code remains the same!

Don’t forget the manifest!

LMSs require that SCORM courses include an imsmanifest.xml file. You will need to include one to get this example working on an LMS. The imsmanifest files vary greatly between SCORM 1.2 and SCORM 2004 (SCORM 2004 uses much more complex manifests).

The final product, including the imsmanifest.xml file, the SCORM wrapper and HTML file can be downloaded free from the downloads page.

Feedback appreciated

To borrow a line from A List Apart, “was it good for you, too?” Please let me know. Until next time…

How to add basic SCORM code to a Flash movie

Update 10/2011: The Planets example has been updated (almost completely rewritten) and no longer strictly adheres to the steps and screenshots in this tutorial. The general concepts are the same, but the project files have been substantially refined. To prevent confusion about which files to use, I have removed the original project files and replaced them with the updated version. Sorry for any inconvenience, and you’re welcome!

Here’s a quick tutorial for adding basic SCORM functionality to an existing Flash file. This tutorial aims to demonstrate just how easy it can be to add SCORM functionality to an existing Flash movie.

In this tutorial, we’re going to keep things very simple; our SCORM code will only check the LMS for a prior completion, and if no completion is found, will set the course to complete at the appropriate point in the movie.

Here are the work files (ZIP, approx 615KB) if you’d like to add the code yourself while reading the tutorial. The zip file also contains the completed product.

Important note: This tutorial uses ActionScript 3 and SCORM 1.2, but the same principles apply for ActionScript 2 and SCORM 2004

The steps:

  1. Add the SCORM wrapper to the head of the HTML file
  2. Import the SCORM class into the Flash file
  3. Add some variables and create a SCORM instance
  4. Initialize the SCORM connection and check for prior course completion
  5. Add the SCORM completion code
  6. Publish the FLA
  7. Modify the manifest

Step one: Add the SCORM wrapper to the head of the HTML file

Open the index.html file in your HTML editor of choice. Note that we’re NOT using the standard HTML file produced by Flash’s publishing feature. It’s my opinion that the HTML produced by Flash is ugly, bloated, and doesn’t support standards well enough. We’ll roll our own using stripped-down markup, external CSS file for simple styling, and SWFObject for embedding (feel free to use another embedding method if you prefer).

Once you’ve opened the HTML file, add a link to the SCORM wrapper script in the document’s <head>:

<script type="text/javascript" src="SCORM_API_wrapper.js"></script>

Your HTML file should look something like this:

Add a link to the wrapper JavaScript file in your HTML

Update: This screenshot is slightly out-of-date; the SCORM wrapper file no longer includes the version number in the filename, and should just be SCORM_API_wrapper.js

You may want to specify the targeted SCORM version using JavaScript (this can help avoid problems with some LMSs). To do so, simply add the following line of code to the head of the document after the SCORM_API_wrapper.js link:

<script type="text/javascript">pipwerks.SCORM.version = "1.2";</script>

Believe it or not, that’s the only change that needs to be made to the HTML file! Save and close the file.

Update: The JavaScript in the project files has been expanded to include a few other best practices, including using an onunload handler.

Step two: Import the SCORM class into the Flash file

Open the planets.fla file in Flash. Add the SCORM class to the Flash file using an import statement in Frame 1’s frame script:

import fl.controls.Button;
import flash.events.MouseEvent;
import com.pipwerks.SCORM;

The file should look something like this:

Add the import statement

Update: The latest version of the SCORM Wrapper for ActionScript uses a slightly different path than the one in the screenshot: com.pipwerks.SCORM instead of pipwerks.SCORM.

This is a good time to test the FLA to ensure you have the correct file path for the SCORM class (it should be in a folder named com, which is inside a folder named pipwerks; this pipwerks folder should be located in the same folder as the FLA file). To test the FLA, go to Control > Test Movie. If the movie plays without errors, your file paths are ok.

Step three: Add some variables and create a SCORM instance

Declare the following variables in the first frame of your Flash file, after the import statements:

import fl.controls.Button;
import flash.events.MouseEvent;
import com.pipwerks.SCORM;

var lessonStatus:String;
var lmsConnected:Boolean;
var success:Boolean;

Next, you’ll need to create a new SCORM instance using the pipwerks.SCORM class. You can create a new SCORM object using the following code:

import fl.controls.Button;
import flash.events.MouseEvent;
import com.pipwerks.SCORM;

var lessonStatus:String;
var lmsConnected:Boolean;
var success:Boolean;
var scorm:SCORM = new SCORM();

Update: The FLA’s ActionScript has been rewritten and has a slightly different structure than the code presented in the rest of this post, but the same principles apply.

Step four: Initialize the SCORM connection and check for prior course completion

Add a scorm.connect() call, which returns a boolean indicating whether it succeeded or not.

import fl.controls.Button;
import flash.events.MouseEvent;
import pipwerks.SCORM;

var lessonStatus:String;
var lmsConnected:Boolean;
var success:Boolean;
var scorm:SCORM = new SCORM();

lmsConnected = scorm.connect();

If the connection was successful, lmsConnected will evaluate to true. That means we can start requesting data from the LMS. Start by requesting the current completion status.

A few things to note: If the course status is “completed” or “passed”, we won’t need to keep the LMS connection active — we need to be careful not to overwrite the previous completion by accident. So, if the course has already been completed, we’ll just disconnect and call it a day.

If the completion status isn’t “completed” or “passed”, we’ll need to explicitly set the course to “incomplete”.

import fl.controls.Button;
import flash.events.MouseEvent;
import pipwerks.SCORM;

var lessonStatus:String;
var lmsConnected:Boolean;
var success:Boolean;
var scorm:SCORM = new SCORM();

lmsConnected = scorm.connect();

if(lmsConnected){

   lessonStatus = scorm.get("cmi.core.lesson_status");

   if(lessonStatus == "completed"){

      //Course has already been completed.
      scorm.disconnect();

   } else {

      //Must tell LMS course has not been completed yet.
      success = scorm.set("cmi.core.lesson_status", "incomplete");

   }

} else {

   trace("Could not connect to LMS.");

}

Step five: Add the SCORM completion code

Find the appropriate place in your movie to call the completion code. In this example, we’ll call the completion code when all four planets have been visited. There is already a ‘check’ for this condition in the function resetPlanets, so we can just add the code there.

function resetPlanets():void {

if(visitedMercury && visitedVenus && visitedEarth && visitedMars){

   success = scorm.set("cmi.core.lesson_status", "completed");
   scorm.disconnect();
   lmsConnected = false;

   gotoAndPlay("end");

} else {

[ ... ]

Step six: Publish the FLA

Publish the FLA. Be sure to turn OFF the “HTML” option since we’re using our own HTML file. You should also ensure the target Flash version is Flash 9, since the “Planets” movie uses ActionScript 3 and a few filters that are only supported by Flash 9+.

Save and close the FLA.

Step seven: Modify the manifest

All SCORM-based courses require a manifest file (imsmanifest.xml) that contains important metadata about the course. For our example, we’ll simply grab an existing imsmanifest.xml file and update it to match our course.

  1. Open the imsmanifest.xml file
  2. Change the identifier attribute of the manifest element (at the top of the file) to something suitable for this course (no spaces): identifier="MyPlanetsCourse"
  3. Find the organizations element (and organization child element) starting at line 15. Change the “default” and “identifier” attributes to something suitable for your organization. I’ll use “pipwerks”. Be sure to avoid spaces and illegal characters, such as punctuation (other than the underscore _)
  4. Find the two title elements, starting at line 17. Change both of them to something suitable for your course. For this example, I’ll change them both to “Planets!”
  5. You’ll need to list the files used by this course in the resource node. For this example, we need to make sure “href” is set to “index.html”, then we need to list the other files using file elements:
    
    <resource identifier="SCO_Resource_01" type="webcontent" adlcp:scormtype="sco" href="index.html">
       <file href="index.html"/>
       <file href="planets.swf"/>
       <file href="SCORM_API_wrapper.js"/>
       <file href="swfobject.js"/>
    </resource>
    
  6. Save and close the imsmanifest file

Wrap-up

That’s all there is to it! As you can see, adding simple SCORM code is much easier than many people realize. It may seem daunting at first, but in reality all we’ve done here is:

  • Added a little JavaScript to the HTML file
  • Added a few variables and functions to the ActionScript
  • Edited a few IDs and file links in the imsmanifest.xml file

In my opinion, SCORM only becomes difficult if you try and use it to handle a course’s sequencing and navigation, which even SCORM experts are hesitant to do (it’s considered a “broken” feature by many key figures in the industry).

The bottom line is that if your existing FLA is self-sufficient before SCORM comes into the picture — it’s already set up to handle its own navigation internally via ActionScript and already has a mechanism for determining whether the user has ‘finished’ the movie, be it completing an activity or simply reaching the last frame of the SWF — SCORM becomes more of drop-in item, almost an afterthought. It doesn’t need to be a nightmare that scares developers away.

It’s my hope that my SCORM wrapper and ActionScript classes encourage more people to embrace SCORM as a simple, easy way to ensure their course(s) use standards and work in almost any LMS.

Creating simple ungraded quiz questions with feedback using HTML and JavaScript

Clive Shepherd recently wrote about “inductive learning.” He said:

In my post last week, Whatever happened to inductive learning?, I complained how difficult it was with current rapid development tools to write more conversational inductive questions in which you as author are able to comment on each selection that the user makes.

He explained that he wasn’t looking for graded answers, just a method for providing custom feedback for each clicked answer. He then provided some examples he created in Captivate and Articulate Presenter.

When I read his post, the first thing I thought was how dead-simple it is to create that kind of interaction in HTML and JavaScript. Flash-based products are not (as of this writing, anyway) really built for doing this kind of work, so why bother?

I think the biggest argument for trying to shoehorn this kind of interaction into a Flash-based system is the graphical user interface (GUI)… the avoidance of working directly with code. The Flash-based systems have nice GUIs, while the HTML-based systems are generally outdated and clunky.

Well, for those who don’t like working with code, I feel for ya; I honestly understand where you’re coming from. But, you sometimes need to go outside of your comfort zone to achieve your goals.

For those who are willing to give it a shot, I present to you a simple HTML and JavaScript template for inductive learning-style questions (ungraded, customized feedback for each answer selected).

This is a simple demo, but because it’s built in HTML with CSS and JavaScript, it’s completely customizable and can be restyled and/or re-purposed for a variety of question types. JavaScripters can even add SCORM calls if desired! I’ve used this approach successfully at work, though my version at work uses MooTools effects to give the interactions a much smoother, Flash-like feel.

Enjoy! Please let me know if you find these examples helpful, or if you have other ideas for this kind of question style.

HTML 5: The strong element

I just saw something interesting I thought I’d pass along. In the new HTML 5 proposal, the strong element is being modified to represent “importance rather than strong emphasis.”

The WHATWG gives the following example:


<strong>Warning.</strong> This dungeon is dangerous. 
<strong>Avoid the ducks.</strong> Take any gold you find. 
<strong><strong>Do not take any of the diamonds</strong>, 
they are explosive and <strong>will destroy anything within 
ten meters.</strong></strong> You have been warned.

The b element is supposed to represent “a span of text to be stylistically offset from the normal prose without conveying any extra importance, such as key words in a document abstract, product names in a review, or other spans of text whose typical typographic presentation is boldened.”

The WHATWG gives the following example:

The <b>frobonitor</b> and <b>barbinator</b> components are fried.

To me, this is both exciting and confusing. Knowing we can nest the strong element opens up some new styling possibilities while keeping the markup valid and semantic. But using both the b and the strong elements, with different conditions for their usage, will ultimately be confusing for most people, including me.

Wonder how WYSIWYG editors will handle this.

Vertical centering — without using tables!

For many years, table-based web page layouts were the rule, not the exception. They were easy to build, they worked cross-browser, and WYSIWYG editors made it a breeze to create and edit tables. CSS-based layouts didn’t really grab hold until just a few years ago, thanks to the evangelism of people like Jeffrey Zeldman and Eric Meyer.

The benefits of CSS-based layouts are very well-documented, and include increased accessibility (for surfers who use assistive technology), ease of site updates (change an entire site’s look and feel with a single stylesheet), and improved search engine rankings (search engines are able to index your content more cleanly when it isn’t surrounded by table markup).

It’s getting easier and easier to avoid table-based layouts these days, and even WYSIWYG editors like Dreamweaver have shifted gears and started including CSS-based layout templates with their software. This is way cool.

But every now and then, a developer like myself will come up against something that was SOOOO easy with table-based layouts and winds up being a royal pain with CSS-based layouts. One of these “d’oh!” moments is when you try to vertically center an element on your web page. Umm… hang on, let me rephrase that: One of these “d’oh!” moments is when you try to vertically center an element on your web page when using Internet Explorer 6.

Firefox supports CSS standards better than Internet Explorer 6 (the dominant browser), and predictably, vertical centering in Firefox is a piece of cake!

This journal entry is devoted to explaining how to get vertical centering working in both Firefox and IE6.

First, an example page.

The ingredients

There are three keys to making this work:

  1. A set of nested DIVs (yes, I know some of you are anti-nested DIV, but hey the world keeps on spinning).
  2. CSS for standards-compliant browsers (Firefox et al)
  3. A separate chunk of CSS for our old friend IE6

Nested DIVs

The first requirement is nested DIVs. You may be thinking “what’s the point of avoiding tables if I still have bloated markup?” My response is that one small set of nested DIVs isn’t nearly as bloated as a table, and using DIVs still helps you keep your markup readable for accessibility purposes and search engines. It’s a good thing! DIVs are also much more flexible should you decide to change your layout later on.

Here’s the layout in all its glory:


<body>
<div id="outer">
  <div id="container">
    <div id="inner">
      <img src="http://pipwerks.com/images/posts/indicator_green.gif" alt="Please wait" width="32" height="32" /><br />
      Loading...<br/>
      Well, not really.<br/>
      But don't I look nice centered like this?
    </div>
  </div>
</div>
</body>

The ‘inner’ DIV is what will hold your content. The ‘outer’ and ‘container’ DIVs’ sole purpose in life is to get your content centered on the page! I’ll explain this in a moment.

The CSS

Let’s look at the CSS:


body {
}

* {
   margin: 0;
   padding: 0;
}

/* macs won't see this! */
html, body { 
   height:100%;
   width:100%;
}
/* END mac */

#outer {
   height:100%;
   width:100%;
   display:table;
   vertical-align:middle;
}

#container {
   display:table-cell;
   vertical-align:middle;
}

#inner {
   text-align: center;
   width: 50%;
   margin-left:auto;
   margin-right:auto;
}

We set all elements on the page to have a default padding and margin of 0. This avoids box-model issues. We also dealt with some Mac inconsistencies with the Mac hack.

As you can see from the CSS, the body and outer elements (outer, container) are set to be 100% wide and 100% tall. The outer element is told to behave like a table (display:table), and is vertically centered using the “vertical-align:middle” property.

By nesting the ‘container’ DIV inside the faux-table, we can make the container DIV behave like a table cell: “display:table-cell”. This DIV is also set to be vertically centered using the “vertical-align:middle” property.

That’s it for the vertical centering in CSS-compliant browsers!

In this case, the ‘inner’ DIV’s CSS is purely for horizontal centering. I set the width to 50% simply because I wanted a narrow DIV, and I centered the DIV horizontally using margin-left: auto and margin-right: auto.

Remember, the code up to this point is for standards-compliant browsers, not IE6.

Dealing with our friend, Internet Explorer 6

Now, you may be thinking that writing different CSS specifically for IE harkens back to the bad old days of browser sniffers and alternate web sites, and you would be correct. However, the alternate CSS is very easy to manage, and doesn’t require a browser sniffer! Thanks to Microsoft’s implementation of conditional comments in IE, we can simply insert a chunk of alternate CSS code into a conditional comment… whatever is inside the comment will be safely ignored by any non-IE browser. Here’s an example:


<!--[if IE ]>
   <style type="text/css">
      body {
         color: #ff0000;
      }
   </style>
<![endif]-->

You can read more about conditional comments at Peter-Paul Koch’s website.

On our vertical centering example, I used the following CSS code in a conditional comment:


#container {
   height: 1px; /* required for IE to properly center vertically */
   position:relative;
   top:50%
}

#inner {
   position:relative;
   top:-50%;
}

Internet Explorer doesn’t support the “display: table”, “display: table-cell” and “vertical-align: middle” properties. To get around this, we have told IE to use relative positioning to move the ‘container’ DIV 50% down from the top of its parent DIV (‘outer’), and to move the ‘inner’ DIV negative 50% from the top its parent DIV (‘container’).

Normally these two values would simply offset each other and cause the ‘inner’ DIV’s content to be displayed at the top of the screen. However, through the magical glitches of IE, by setting the ‘container’ DIV’s height attribute, the DIV suddenly jumps down to the middle of the page, right where we want it to be! I have no idea why, but hey, it works. Here’s the proof.

Important: the height must be set BEFORE the top percentage is declared.

Externalizing the CSS

It’s always a good idea to set up your CSS in external CSS files. This helps keep your web page clean, enables you to re-use your code on other pages, and much more. The CSS contained in the conditional code can also be placed in an external stylesheet. Here’s how I set up my page:


<link href="centering.css" rel="stylesheet" type="text/css" />

<!--[if IE ]>
   <link href="centering-IE.css" rel="stylesheet" type="text/css" />
<![endif]-->

The CSS is exactly the same, but cut and pasted into two external files: centering.css and centering-IE.css.

Now my page is looking clean as a whistle, and I can reuse my code by linking my other pages to my ‘centering’ css files!

Cool, can you do that with a Flash SWF, too?

Yes, you can! Here’s an example.

The code for the Flash example is exactly the same as the first example, except for content: I replaced the contents of the ‘inner’ DIV with some dummy text, and I embedded a Flash SWF using Geoff Stearn’s SWFObject method (the best embedding method I’ve used by far!). No CSS was altered except for the body and font colors.

And there you have it… a nice, (relatively) simple way to vertically center DIVs using CSS and NO tables.

Updates

Thanks to some sharp-eyed readers, here are a few corrections and/or additions:

Update: Here is an example of a Flash SWF embedded with a transparent background. The CSS has been altered slightly: an HTML background image has been specified, and the inner DIV has been set to the exact pixel size of the Flash SWF; this properly centers the SWF and causes scrollbars to appear when a minimum width or height has been reached. Tested successfully in IE6, FF2 and Safari 3 (beta), all Windows XP.

Update 9/03/2007: For IE7, if specifying the height of the inner div, you must also specify the height of the container div in the IE-specific CSS file. For example:

In standard CSS:


#inner {
   text-align: center;
   width: 550px;
   height: 400px;
   margin-left:auto;
   margin-right:auto;
}

In IE7’s CSS:


#container {
   /* required for IE to properly center vertically,
      must match #inner height */
   height: 400px;
   position:relative;
   top:50%
}

Unfortunately, in IE7 this also produces scrollbars (IE7 thinks the page content is larger than it actually is). I plan to do some more research into this when I have the time.