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.

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.

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.

Rule of Thirds tutorial

By popular demand, I’m re-posting the Rule of Thirds tutorial.
I took it down when I revamped my site, largely because the tutorial is old and requires Shockwave. A few school teachers have let me know they used it in their classes, so I’m happy to repost it.

Rule of Thirds
Launch the Rule of Thirds tutorial
Also available: Standalone .exe version (Windows only)

Background: Rule of Thirds is quick and fun tutorial explaining the concept of the rule of thirds as used in visual design. I created this tutorial for a graduate-level Macromedia Director course at San Francisco State University in 2004. Because it was created in Macromedia Director, you’ll need the Shockwave plug-in to view the file (sorry about that… I know it’s a hassle).

Some materials contained in project (music, images) are owned by others. This was a non-profit educational project that respects their rights and has made use of their materials only for fair-use demonstration purposes. All other materials © Philip Hutchison 2004.

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.

Viewing PDFs in a Browser on a Mac

As a Mac user, one of the more annoying issues I frequently encounter is funky PDF handling in Firefox and Safari. For instance:

  • Adobe doesn’t make a version of Adobe Reader that’s compatible with Firefox on Mac OS X
  • Adobe Reader is only supported in 32-bit versions of Safari on OS X (Snow Leopard ships with a 64-bit version of Safari)
  • Safari has built-in handling of PDFs, but if Adobe Reader is installed — whether it’s actually working in Safari or not — it will turn off Safari’s native PDF handling by default.

Here are some things you can do to get PDFs to display in your browser(s).

Restore Safari’s built-in PDF handling

If your Safari browser isn’t using OS X’s native PDF handling, Adobe Reader may be overriding it. Here’s how to fix it:

  1. Launch Adobe Reader
  2. Go to Preferences > Internet
  3. Deselect “Display PDF in browser using…”
  4. Click OK

Enable PDF support in Firefox

Mozilla has a handy reference for this topic.  Here’s what they say:

Adobe does not yet maintain a plugin for viewing PDF files within Firefox for computers with Mac OS X. To view PDFs in Firefox:

I’ve installed the Firefox PDF Plugin on my Mac (Firefox 3.6), and it works great. It uses the Mac’s built-in PDF handling, so it’s a very small plugin runs very quick.

Caveats and an editorial

Using the Mac’s built-in PDF support means you won’t be able to take advantage of some of Adobe Reader’s new features, such as scripting, portfolios, and SWF support.

I don’t know about you, but I prefer to use the PDF format for its traditional purpose: reading print-based documents online.  Plus, Adobe Reader currently requires 290MB of space on the hard drive. I can live without the new features and would prefer to use my 290MB of space for other things.

In general, I recommend avoiding Adobe Reader on a Mac because of its poor support in Firefox and Safari, its incredible bloat, and the seemingly daily announcements of major security vulnerabilities. Most of the security issues have been directly related to the scripting functionality and other new features. If a lite version of Adobe Reader were offered — one that removes the bloat, eschews these new features, and simply lets us view standard PDF documents — I’d be more than happy to use it.

Shameless plug

Need to embed a PDF in an HTML page? Try PDFObject. It’s free, tiny, and works much like SWFObject (the two projects are unrelated).

Mac Mini Server

In my last post, I explained my frustration with web hosting services and how this frustration led me to purchase my own server. In this post, I will briefly touch on some of the ups and downs I’ve experienced with the Mac Mini Server and its Snow Leopard Server software.

Hardware

The Server Edition of the Mac Mini is very similar to a normal Mac Mini, with a few notable exceptions: it has 4GB RAM, it does not have an optical (DVD) drive, and it comes equipped with a second 500GB hard drive for a total of 1TB. If you’ve never seen a Mac Mini in person, it truly lives up to its name… this bugger is TINY. It also uses very little electricity compared to other PCs.

RAM

I’ve read that the Mini is actually capable of using up to 8GB of RAM, though Apple doesn’t officially support it yet. I didn’t want to try it, as I believe 4GB should be enough for my needs. I don’t plan on doing anything fancy besides serve my websites.

No Optical Drive

This is a bit of a pain sometimes, but in my case I’ve shipped the Mini off to a data center, so I couldn’t plop a DVD into the optical drive if I wanted to!  The Mini uses the same technology as the MacBook Air for accessing optical drives in other computers. You also have the option of hooking up an external USB drive if you need to.

Since the Mini came with Snow Leopard Server pre-installed, I wasn’t worried about the optical drive.

Dual hard drives

The Server Edition comes with dual 500GB drives for a total of 1TB of disk space. Of course, the operating system eats up a chunk of that space, so you’ll have less than 1TB of free space to work with.

Both drives are 5400 RPM, which I didn’t notice until after I bought it. I was a bit disappointed about this, as we all know 7200 RPM drives are much more responsive. However, on the bright side, 5400 RPM drives don’t run as hot and won’t use as much electricity. If I were using this Mini for a major corporate site, I’d bump up the hard drives, but for my purposes two 500GB 5400 RPM drives will be fine.

As for how the drives are configured, it appears Apple intended the second drive to be for content storage (Snow Leopard Server is heavy on content creation tools). Since I don’t plan to use much space (for now), and because I’m completely paranoid about losing my data, I’ve repurposed the second drive as a Time Machine backup drive.  I’ve also hooked up an external 500GB USB drive for bootable SuperDuper backups. Redundant backups are a good thing, right?

By the way, I’ve read that an external 7200 RPM hard drive connected via the FireWire 800 port can beat the internal 5400 RPM drive’s performance.  You’ll be able to upgrade pretty easily if you need to.

Other hardware notes

Mac Minis don’t come with keyboards, a mouse, or a monitor. You’ll need to get those yourself.  Since mine is located at a remote data center, I connect to it remotely using my Macs at home. Works great.

Software

Snow Leopard Server

The Snow Leopard Server (SLS) marketing materials had me drooling. I know that Linux is no fun. Mac OS X is a variation of Linux, and it’s way fun. So it was logical to assume that Apple would do for Linux servers what they did for Linux desktops.

Umm… not quite.  Actually, it’s kind of a big fail.

The bad

SLS is nowhere near as easy to configure as Apple makes it sound. Apple positioned the Mac Mini/SLS combo as if you can be your own IT department. True, it does provide many very cool enterprise-level tools and services, but if you don’t have previous system administrator experience on a Unix/Linux system, you are in for a world of confusion. For instance, if you intend to host more than one website, you’ll need to get out of the “one-click” Server Preferences app and dig into the advanced settings in the Server Admin app. If anything gets messed up here, you’ll probably have to go to the command-line to make fixes. While on the phone with Apple Support.

SLS uses the Workgroup Manager app to manage login accounts. This is a front-end GUI for a heavily customized Open Directory authentication system. Don’t know what Open Directory is? Then you’ll especially hate trying to set this stuff up. Unix/Linux uses POSIX permissions for users. These are what you see when you use most FTP programs or even WordPress. Windows uses access control lists (ACL) for user permissions, as they provide a finer level of control than POSIX over what people can access. Mac OS X uses BOTH. This causes a great deal of consternation when trying to set up FTP services for your users, as the POSIX settings may look correct, but the ACL settings will override the POSIX! I still haven’t gotten mine sorted out yet. It’s a real pain.

Long story short, Snow Leopard Server is very confusing to administer. And since it varies so much from the traditional Unix/Linux setup, it’s very hard to find help. For instance, file paths for Apache (the software that serves web pages) are completely different in Snow Leopard Server than they are in a typical Linux environment. If you have an Apache how-to guide, a significant chunk of the info will be wrong for your SLS system because of different file paths, or Apple-specific security restrictions, or Apple’s unique implementation of add-ons like PHP.

I often feel that I may have been better off ditching Snow Leopard Server and installing a typical Linux server system. Which, I might add, is completely free (Apple charges $499 for SLS if purchased separately from the Mac Mini). If I had done a typical Linux install, I would have had a much easier time finding answers online or in books. I’ve purchased two Snow Leopard Server books, and while they’ve been a little helpful, they’re still incredibly dense.

I should mention one bright spot for Snow Leopard Server support: Apple provides free 90-day phone support for Snow Leopard Server when you buy a Mac Mini Server. I found the phone support to be very helpful a couple of times, and completely exasperating a couple of times. It’s like any other company’s tech support: some guys know their stuff and are very helpful, and others are either ignorant or standoffish, treating you like you’re an idiot. Par for the course, I suppose.  One major problem I had with the phone support was that Apple apparently doesn’t have the Mac Mini Server Edition in its phone tree. When it asks you what product you’re calling about, if you say “Mac Mini” you’re taken to the wrong line (I speak from experience). If you say “Snow Leopard Server” it always thinks you’re calling about the non-server edition of Snow Leopard. I’ve yet to make a call to Apple support where I didn’t have to explain my hardware/software setup and be redirected a couple of times.

The good

Snow Leopard Server includes some tantalizing features that almost make it worth the trouble of dealing with setup/administration. Here’s a quick list:

iCal Server, Address Book Server, Mail services: Apple’s version of a Microsoft Exchange system.

  • iCal Server is similar to Outlook’s calendar system. It’s great for businesses where everyone needs their own calendar, including the ability to send meeting requests and whatnot. My take? I don’t have iCal Server enabled because it’s easier to use a service like Google Calendar, and you don’t have to worry about administration headaches. However, iCal allows you to be in complete control over security and privacy, if that’s an issue for you. It also syncs with iPhones and MobileMe.
  • Address Book Server is a similar deal: It allows multiple users to maintain address books online, and they can all be synced with iPhones and MobileMe. Makes sense for a business, but not worth the effort for an individual like me.
  • Mail services. This is a biggie. Mail services allows you to create mail accounts for every user in your system. It has all kinds of features, but to be honest, it’s such a pain to administer that I mostly stick to my GMail account. Again, if you’re a business, this is a great app. Keep in mind that it’s a repackaging of a free Unix-based application named Postfix, which means you could have similar functionality free without buying Snow Leopard Server, it just won’t look as pretty. And yes, Mail includes webmail access and spam filtering.

Podcast Producer, Wiki Server, iChat Server: Apple jumping on to the Social Media bandwagon.

  • Podcast Producer is an interesting application. You can record podcasts using your desktop Mac, then upload them to the server where they’ll be encoded and served to others (much like your very own YouTube). I find it alluring, yet I currently don’t have much need for it. I do plan to play with it at some point… it has great potential for e-learning.
  • Wiki Server. For better or for worse, every website you create in Snow Leopard Server has a wiki enabled by default. These wikis are powered by Wiki Server. Wikis are certainly useful, and the wiki system in SLS is probably the easiest and lowest-hassle feature of the entire system. It just works, and it’s very pretty compared to most wiki systems. The problem for me is that I often don’t want a wiki attached to my sites. This means I have to manually go in to each site and turn off the wiki feature. You got it — the one thing that works great out-of-the-box is the one thing I don’t really want to use!
  • iChat Server. I didn’t understand why anyone would want an iChat Server. Then I realized that it includes video chat support, and it can be completely encrypted. That is pretty cool. You can have private video chats with anyone, and since you’re using your own server and encrypted video, you can rest assured your video is not being monitored or cached on some corporation’s server. Unless you’re being monitored by a government agency or something. In that case, I don’t know you.
  • Client Management. This is where Snow Leopard goes enterprise. You can remotely administer Macs on your network, including performing system updates and remote software installation. You can also manage iPhones. This is certainly not something I’m in need of, but if you’re a business (or school) I can see this being a big plus.

VPN, Time Machine, File Sharing: Essential tools for daily work.

  • VPN. This is a great feature for people concerned about privacy. Most people know that VPN allows you to connect to your workplace’s network via an encrypted connection. But did you know you can use VPN to secure your data over WiFi connections? When you connect your laptop to your server via VPN, all of the data transmitted between the two computers is encrypted. So if you’re using WiFi at a conference or a coffee shop (or even at home) and want to make sure someone isn’t intercepting and reading your data, use VPN to encrypt those transmissions and protect yourself.
  • Time Machine. Every Mac comes with Time Machine, but Snow Leopard Server’s Time Machine lets you back up your Mac’s hard drive to the server, no matter where you are. No USB cables, no external hard drives… all you need is an internet connection (preferably using VPN as described above).
  • File Sharing. Snow Leopard Server’s file sharing feature is nothing new, but so far it seems quite sturdy and is flexible enough to work with both Mac and Windows systems.

Snow Leopard Server: Yea or Nay?

Do the pros of using Snow Leopard Server outweigh the cons? I’m not sure I can answer that question yet. I haven’t tried using every feature available, as I’m taking baby steps to ensure I don’t accidentally kill my system. I’ve got the basics up and running (web server, MySQL, file sharing), and so far it’s been running pretty smoothly. I’ve been pretty hands-off. I still don’t have FTP set up the way I’d like, and there are a few other odds and ends that need fixing, but on the whole everything seems pretty stable right now.

If you ever decide to get Snow Leopard Server, do yourself a favor and do a test run before deploying it.  Go ahead and set it up in your living room, and try to get every feature up and running. Chances are you’ll make some critical mistakes, and hopefully you’ll learn from them. By the time you deploy the server in a production environment you’ll be an old pro.

If you do your first setup in a production environment, you are guaranteed to run into trouble. When I called Apple support the first time, the first thing they suggested was for me to reinstall the system. Seriously. Multiple techs informed me that because of the intricacies of the underlying Unix system, if you fudge some of the key settings it’s much quicker and safer to reinstall than to try and fix the problem(s).

Running your own server from a data center

As I mentioned in my last blog post, I decided to get the Mac Mini because of my frustration with commercial web hosts. Jon Gruber pointed out an article written by Brian Stucki, who owns macminicolo.net. I was intrigued by the idea of having my own computer in a high-class data center. I set up an account, mailed in my Mac, and next thing you know I have a server!

Brian has been pretty helpful getting my hardware up and running, and as you’d expect, his company has been pretty hands-off when it comes to administering the software side of things. It was Brian who suggested the external drive with the SuperDuper backup. As they say in Hawaii, akamai!

I did have a hiccup, which was no fault of macminicolo — my Mac Mini apparently got damaged during shipping and wouldn’t boot. Luckily for me, Brian is a regular at the Apple Store in Las Vegas (where the data center is located), and they hooked him — and me — up with a new server on the spot, no charge. Brian wasn’t obligated to go to the Apple Store on my behalf, but he did, and at no charge, so props to him and his excellent customer service.

Now that I’ve had a server up and running for a couple of months, I can tell you one thing: whether you have Snow Leopard Server or a plain Linux server, you will LOVE the freedom you have to configure the server to meet your precise needs. Want to set up Rails? No problem. Want to try server-side JavaScript? No problem. Want to set up a custom web service API?  No problem. You can do any experiment you like, any time, with no bureaucracy or extra fees — assuming it’s legal and doesn’t violate your data center agreement!

If you use a hosted web service, especially a shared hosted service, chances are you will be limited to whatever they give you. Having your own server means freedom. Unfortunately, it also means more work. Macminicolo’s hosting prices are slightly more expensive than a typical shared hosting plan, but it’s cheaper than my AT&T iPhone plan, so I can’t complain.

Alternatives

The Mac Mini Server Edition is currently $999, and includes Snow Leopard Server.  If you’re looking for a cheaper alternative, you could buy a standard Mac Mini instead. They start at $599 and have nearly identical hardware, except for the amount of RAM and hard drives, both of which you can upgrade on your own for a fairly low price. If you get a standard Mac Mini, you’ll be getting the desktop version of Snow Leopard, in which case you might want to check out MAMP. You could also replace your Mac OS with a Linux server system like Ubuntu server edition. Or you could install Linux server software as a virtual machine using VirtualBox (free), VMWare or Parallels. In this scenario, you can easily replace or upgrade the server software without having to reinstall your entire operating system.

Final words

Am I glad I did it? You betcha. Was it as easy as I had hoped? Not at all. Would I recommend it to non-technical people? Probably not. You have to have a decent level of technical savoir-faire and a good amount of stick-with-it-ness. However, once you get over the initial hurdles, it mostly runs on its own. Snow Leopard Server can even email you when there are patches that need to be installed, so it isn’t like you need to log in as an administrator every day. On the whole, I’m pretty happy and I’m glad I took the plunge.

Gotchas in Internet Explorer 8

Internet Explorer 8 (IE8) is at Release Candidate 1, which means it will be released very shortly. IE8 is a brand-new browser and will represent a considerable shift from IE7/IE6; it will follow standards more closely and will offer much improved CSS 2.1 support. However, because of some of these changes, it is also widely understood that IE8 might break websites that have relied on IE-specific hacks targeted at previous versions of Internet Explorer.

To their credit, the IE development team has been very candid about the changes and have posted a number of blogs and documents aimed at helping web developers prepare for IE8. I was looking over one such page and thought I’d point out what I consider to be some of the biggest ‘gotchas’ so far.

Setting Unsupported CSS Values

Trying to detect support for a specific CSS value through a JavaScript try/catch statement will no longer generate an exception, which means you can’t rely on JavaScript to detect support for specific CSS values anymore.

Assigning CSS values that were unsupported in IE7 but are supported in IE8 Standards Mode will not generate exceptions in IE8 Compatibility View. Some sites use these exceptions to determine if a particular value for a CSS property is supported or not.


try {
   elm.style.display = "table-cell";
} catch(e) {
   // This executes in IE7,
   // but not IE8, regardless of mode
}

Malformed HTML

IE8 will not be as forgiving of malformed HTML markup. This is a great new ‘feature’ in terms of ensuring people (and software) are less sloppy with their markup, but this will certainly cause many, many problems for hundreds of thousands of old, poorly written websites.

Parser error correction for malformed HTML has changed in IE8 Standards Mode. Pages depending on the way IE7 performs error correction may encounter issues as a result.


<ul>
    <li>1.1
        <ul>
            <li>1.1.1</li>
    </li> <!-- Closes 1.1 in IE8, but not IE7 -->
            <li>1.1.2</li>
        </ul>
    </li>
</ul> 

Working with an Element’s Class

Like the malformed HTML ‘feature’, this is another great improvement in IE that will also cause many, many headaches. You see, for years IE wouldn’t let developers use the standard setAttribute("class") method for specifying a class name via JavaScript. Instead, IE required developers to use the proprietary setAttribute("className"). This means that it became commonplace for scripts to check for IE then use class for non-IE browsers and className for IE. Now, you’ll still need to make that check for older versions of IE but find a way to use class for IE8. <sarcasm>This will be fun.</sarcasm>

Don’t get me wrong — I’m excited that IE will finally behave like other browsers in this regard — but it also means that so long as IE6 and IE7 are still around, we’ll have to jump through more hoops to handle class names.

In IE7, “className” had to be used as the attribute name to set and retrieve the class of an element. This has been fixed for standards-compliance in IE8 Standards Mode. Using the old approach will create an attribute named “className” that has no affect on the actual class assigned to an element.


return elm.getAttribute("className");

SOLUTION: Use the standardized name, “class”, instead of “className”.


return elm.getAttribute("class");

CSS Expressions

One of the common hacks for IE’s shortcoming with CSS support has been to use IE’s proprietary CSS expressions, which are basically JavaScript statements embedded in place of a CSS value. While this practice has been frowned upon by most in-the-know web developers, it still wound up being heavily utilized as an ‘easy fix’ type of hack.

IE8 will no longer support CSS expressions. This will make it behave more like other browsers, but will cause problems for those who have relied on CSS expression hacks. Fortunately, it should be relatively easy to move your CSS expressions into your page’s JavaScript as suggested by Microsoft.

Support for CSS Expressions has been removed in IE8 Standards Mode.


/* CSS */
#main {
    background-color: expression(
        (new Date()).getHours()%2 ? "#000" : "#fff"
    );
}

SOLUTION: Refactor to utilize either improved CSS support or DHTML logic.


/* Script */
var elm = document.getElementById("main");
if((new Date()).getHours()%2) {
    elm.style.backgroundColor = "#000";
} else {
    elm.style.backgroundColor = "#fff";
} 

On the whole, I’m excited about the changes IE8 will bring, although it will undoubtedly require site revisions for anyone who uses JavaScript extensively in their projects.

You can read the original Microsoft blog post here.