function log ( msg ) {
	if ( ! log.log_div ) {
		log.log_div = document.createElement("DIV");
		
		if ( document.all ) {
			log.log_div.style.position = "absolute";
		} else {
			log.log_div.style.position = "fixed";
		}
		
		log.log_div.style.top = "10px";
		log.log_div.style.right = "10px";
		log.log_div.style.background = "#f0f0f0";
		log.log_div.style.border = "1px solid #fff";
		log.log_div.style.color = "#000";
		log.log_div.style.fontSize = "10px";
		log.log_div.style.padding = "5px";
		log.log_div.style.zIndex = "5000";
		log.log_div.style.textAlign = "left";
		log.log_div.style.overflow = "auto";
		log.log_div.style.height = "300px";
		document.body.insertBefore(log.log_div, document.body.firstChild);
	}
	
	if ( ! log.log_messages ) {
		log.log_messages = [];
	}
	
	log.log_messages.push(msg);
	log.log_div.innerHTML = log.log_messages.join("<br>\n");
}

function adjust_text_size ( delta ) {
	var size, match;
	
	match = document.body.className.match(/\badjust_(\d+)\b/);
	if ( match ) {
		size = parseInt(match[1], 10);
	} else {
		size = 1;
	}
	
	size += delta;
	
	if ( size > 3 ) {
		size = 3;
	} else if ( size < 0 ) {
		size = 0;
	}
	
	document.body.className = document.body.className.replace(
		/\badjust_\d+\b/, "adjust_" + size);
}

function validate_form ( form ) {
	var i, valid = true, messages = [], do_alert = false, errors, match,
		error_class = null, no_alert = false, no_messages = false;
	
	errors = form.getAttribute("errors");
	if ( /\bnomessages\b/i.exec(errors) ) {
		no_messages = true;
	}
	
	if ( /\bnoalert\b/i.exec(errors) ) {
		no_alert = true;
	}
	
	match = /\bclass=(\S*)/i.exec(errors)
	if ( match ) {
		error_class = match[1];
	}
	
	function on_error ( message, element, error_element ) {
		if ( error_element ) {
			if ( ! no_messages ) {
				error_element.innerHTML = message;
			}
			
			if ( error_class ) {
				add_css_class(error_element, error_class);
			}
		} else {
			do_alert = true;
		}
		
		messages[messages.length] = message;
	}
	
	function on_valid ( element, error_element ) {
		if ( error_element ) {
			if ( ! no_messages ) {
				error_element.innerHTML = "";
			}
			
			if ( error_class ) {
				remove_css_class(error_element, error_class);
			}
		}
	}
	
	for ( i = form.elements.length - 1 ; i >= 0 ; i-- ) {
		valid = validate_element(form.elements[i], on_error, on_valid)
			&& valid;
	}
	
	if ( ! no_alert && do_alert ) {
		alert(messages.join("\n"));
	}
	
	return valid;
}

function validate_element ( element, on_error, on_valid ) {
	var what = element.getAttribute("validate"),
		initial = element.getAttribute("initial_value"),
		value = element.value,
		error_element, match, onfirm;
	
	if ( ! what ) {
		return true;
	}
	
	error_element = document.getElementById(element.name + "_error");
	
	function error ( message ) {
		try {
			element.focus();
			element.select();
		} catch ( e ) {
			// Ignore errors.
		}
		
		on_error(message, element, error_element);
	}
	
	if ( initial && value == initial ) {
		value = "";
	}
	
	// This can be used to make sure initial_value is set. This is meant to
	// work together with erase_initial().
	if ( /\bhas_initial\b/i.exec(what) && ! initial ) {
		initial = element.value;
		value = "";
	}
	
	if ( /\brequired?\b/i.exec(what) && value == "" ) {
		error("Required.");
		return false;
	}
	
	if ( /\bemail\b/i.exec(what) && value
			&& ! /^[^ \t@]+@[^ \t@]+\.[^ \t@.]+$/i.exec(value) ) {
		error("Not a valid email address.");
		return false;
	}
	
	match = /\bnochange=(\S+)/i.exec(what);
	if ( match && value == match[1] ) {
		// No changes -- don't do further validation.
		on_valid(element, error_element);
		
		return true;
	}
	
	match = /\bconfirm=(\S+)/i.exec(what);
	if ( match ) {
		confirm = document.getElementById(match[1]);
		if ( ! confirm ) {
			confirm = document.getElementsByName(match[1])[0];
		}
		
		if ( confirm && confirm.value != value ) {
			error("Fields do not match.");
			return false;
		}
	}
	
	on_valid(element, error_element);
	
	return true;
}

// Called onfocus, sets up the onblur event. If you use the validation
// functions above, be sure to set the has_initial flag in the validate
// attribute. That way, if the field was never focussed, the validation will
// be able to tell that the field is actually blank.
function erase_initial ( field ) {
	field = field || this;
	var initial = field.getAttribute("initial_value");
	
	if ( ! initial ) {
		field.setAttribute("initial_value", field.value)
		field.onblur = replace_initial;
		field.value = "";
	} else if ( field.value == initial ) {
		field.value = "";
	}
	
}

// Generally this is set up by erase_initial.
function replace_initial () {
	if ( this.value == "" ) {
		this.value = this.getAttribute("initial_value") || "";
	}
}

function fix_hover ( node ) {
	if ( ! document.all ) {
		// Only run in IE. This isn't really an exact enough test.
		return;
	}
	
	var old_mouseenter = node.onmouseenter || function () {};
	var old_mouseleave = node.onmouseleave || function () {};
	
	node.onmouseenter = function () {
		if ( ! has_css_class(this, "hover") ) {
			this.className = this.className.replace(/\S+/g, "$& $&_hover")
				+ " hover";
		}
		
		old_mouseenter.apply(this, arguments);
	}
	
	node.onmouseleave = function () {
		this.className = this.className.replace(/\S+_hover|\bhover\b/g, "");
		
		old_mouseleave.apply(this, arguments);
	}
}

function find_parent ( element, match ) {
	var node;
	
	node = element;
	while ( node.parentNode && node != node.parentNode ) {
		if ( match(node) ) {
			return node;
		}
		
		node = node.parentNode;
	}
	
	return null;
}

function find_descendents ( element, match, count ) {
	var node, found = [];
	
	// count 0 means find all.
	
	node = element.firstChild;
	while ( true ) {
		if ( match(node) ) {
			found.push(node);
			
			if ( count && count <= found.length ) {
				return found;
			}
		}
		
		if ( node.firstChild ) {
			node = node.firstChild;
		} else {
			while ( ! node.nextSibling ) {
				node = node.parentNode;
				
				if ( node == element ) {
					return found;
				}
			}
			
			node = node.nextSibling;
		}
	}
}

function find_descendent ( element, match ) {
	var found = find_descendents(element, match, 1);
	
	if ( found.length ) {
		return found[0];
	}
	
	return null;
}

function match_tag ( tag_name ) {
	tag_name = tag_name.toUpperCase();
	
	return function ( node ) {
		return node.nodeName == tag_name;
	};
}

function match_class ( css_class ) {
	return function ( node ) {
		return has_css_class(node, css_class);
	};
}

function match_node ( to_find ) {
	return function ( node ) {
		return node === to_find;
	};
}

function get_css_classes ( node ) {
	if ( node.className ) {
		return node.className.split(/\s+/);
	} else {
		return [];
	}
}

function has_css_class ( node, class_name ) {
	return new RegExp("\\b" + class_name + "\\b").exec(node.className);
}

function add_css_class ( node, class_name ) {
	if ( node.className ) {
		if ( has_css_class(node, class_name) ) {
			return;
		}
		
		node.className = node.className + " " + class_name;
	} else {
		node.className = class_name;
	}
}

function remove_css_class ( node, class_name ) {
	if ( node.className ) {
		node.className = node.className.replace(
			new RegExp("\\b" + class_name + "\\b"), "");
	}
}

function showBlogEntry(entry_obj_id){
  rollover = document.getElementById(entry_obj_id+"_ro");
  rollover.style.display = 'block';
  rollover.style.zIndex = '99';
  document.getElementById(entry_obj_id).style.zIndex = '99';
}
function hideBlogEntry(entry_obj_id){
  rollover = document.getElementById(entry_obj_id+"_ro");
  rollover.style.display = 'none';
  rollover.style.zIndex = '0';
  document.getElementById(entry_obj_id).style.zIndex = '0';
}


var onload_functions = [];

function register_onload ( func ) {
	onload_functions[onload_functions.length] = func;
}

if ( window.onload ) {
	register_onload(window.onload);
}

window.onload = function () {
	for ( var i = 0 ; i < onload_functions.length ; i++ ) {
		onload_functions[i].apply(window, arguments);
	}
	
	register_onload = function ( func ) {
		throw "window.onload already called; register_onload disabled";
	}
}



register_onload(function () {
	var nodes, i;
	
	if ( ! document.all ) {
		return;
	}
	
	nodes = document.getElementById("menu_bar_right").childNodes;
	for ( i = 0 ; i < nodes.length ; i++ ) {
		if ( nodes[i].nodeType == 1 ) {
			fix_hover(nodes[i]);
			fix_hover(find_descendent(nodes[i], match_class("drop_down")));
		}
	}
	
	nodes = document.getElementById("menu_bar_left").childNodes;
	for ( i = 0 ; i < nodes.length ; i++ ) {
		if ( nodes[i].nodeType == 1 ) {
			fix_hover(nodes[i]);
			fix_hover(find_descendent(nodes[i], match_class("drop_down")));
		}
	}
});


/**
 * Set up hover feature for product features
 */
function setupProductFeatures() {
    // product features
    $$('.product_features .feature').each(function(f) {
      f.onmouseover = function() {
        this.addClassName('hover');

        var r = this.down('.rollover');
        r.style.top = this.offsetTop - 42 + 'px';
        r.style.left = this.offsetLeft + this.offsetParent.offsetLeft + 85 + 'px';
      };

      f.onmouseout = function() {
        this.removeClassName('hover');
      };
    });

    // customer rollovers
    $$('.customer_grid .feature').each(function(f) {
      f.onmouseover = function() {
        this.addClassName('hover');

        var r = this.down('.rollover');
        var topOffset = this.offsetTop;
        var p = this.offsetParent;
        while (p && (p.tagName.toUpperCase() != 'TABLE')) {
            topOffset += p.offsetTop;
            p = p.offsetParent;
        }

        r.style.top = topOffset - 22 + 'px';
        r.style.left = this.offsetLeft + this.offsetParent.offsetLeft + 85 + 'px';
      };

      f.onmouseout = function() {
        this.removeClassName('hover');
      };
    });

    // right side customer rollovers
    $$('.customer_grid .feature_right').each(function(f) {
      f.onmouseover = function() {
        this.addClassName('hover');

        var r = this.down('.rollover');
        var topOffset = this.offsetTop;
        var p = this.offsetParent;
        while (p && (p.tagName.toUpperCase() != 'TABLE')) {
            topOffset += p.offsetTop;
            p = p.offsetParent;
        }

        r.style.top = topOffset - 22 + 'px';
        r.style.left = (this.offsetLeft + this.offsetParent.offsetLeft - 233) + 'px';
      };

      f.onmouseout = function() {
        this.removeClassName('hover');
      };
    });
}

Event.observe(window, 'load', setupProductFeatures);
