Return to Snippet

Revision: 6842
at June 18, 2008 16:57 by johnloy


Initial Code
/**
 * Initializes expanders on this page after the document has been loaded
 */
YAHOO.util.Event.onDOMReady(function()
{
	// remember link location
	var hash = location.hash;

	var expanders = YAHOO.util.Dom.getElementsByClassName('expander');
	for (var i = 0; i < expanders.length; i++) {
		new Mozilla.Expander(expanders[i]);
	}

	// reset link location since the link may have moved on the page
	if (hash) {
		if ((/safari/gi).test(navigator.userAgent)) {
			location.hash = '#nothing'; /* Safari hack */
		}
		location.hash = hash;
	}
});

// create namespace
if (typeof Mozilla == 'undefined') {
	function Mozilla() {}
}

/**
 * Expander widget
 *
 * @param DOMElement container
 */
Mozilla.Expander = function(container)
{
	if (typeof container == 'String') {
		container = YAHOO.util.Dom.get(container);
	}

	// get header and content elements as first two child elements
	var header     = YAHOO.util.Dom.getFirstChild(container);
	this.content   = YAHOO.util.Dom.getNextSibling(header);
	this.container = container;

	if (!this.container.id) {
		YAHOO.util.Dom.generateId(this.container, 'mozilla-expander-');
	}

	this.id        = this.container.id;

	// build header markup
	this.anchor = document.createElement('a');
	this.anchor.href = '#';
	this.anchor.className = 'expander-anchor';
	while (header.firstChild) {
		this.anchor.appendChild(header.firstChild);
	}
	header.appendChild(this.anchor);

	YAHOO.util.Event.on(this.anchor, 'click',
		function(e)
		{
			YAHOO.util.Event.preventDefault(e);
			this.toggleWithAnimation();
		},
		this, true);

	// build content markup
	this.content_animation = document.createElement('div');
	this.content_animation.className = 'expander-animation';

	var content_padding = document.createElement('div');
	content_padding.className = 'expander-padding';
	while (this.content.firstChild) {
		content_padding.appendChild(this.content.firstChild);
	}
	this.content_animation.appendChild(content_padding);

	this.content.appendChild(this.content_animation);

	// prevent closing during opening animation and vice versa
	this.semaphore = false;

	YAHOO.util.Dom.removeClass(this.container, 'expander');

	if ((location.hash.substring(1) == this.id) ||
		this.loadState() == Mozilla.Expander.OPEN) {
		this.open();
	} else {
		this.close();
	}
}

Mozilla.Expander.OPEN           = true;
Mozilla.Expander.CLOSED         = false;
Mozilla.Expander.OPEN_DURATION  = 0.25;
Mozilla.Expander.CLOSE_DURATION = 0.25;

Mozilla.Expander.OPEN_TEXT      = 'Show';
Mozilla.Expander.CLOSE_TEXT     = 'Hide';

Mozilla.Expander.prototype.toggle = function(e)
{
	if (this.state == Mozilla.Expander.OPEN) {
		this.close();
	} else {
		this.open();
	}
}

Mozilla.Expander.prototype.saveState = function()
{
	if (typeof sessionStorage != 'undefined') {
		var href = location.href.split('#')[0];
		var state = (this.state == Mozilla.Expander.OPEN) ?
			'open' : 'closed';

		sessionStorage.setItem(href + '-' + this.id, state);
	}
}

Mozilla.Expander.prototype.loadState = function()
{
	var state = Mozilla.Expander.CLOSED;
	if (typeof sessionStorage != 'undefined') {
		var href = location.href.split('#')[0];
		var loaded_state = sessionStorage.getItem(href + '-' + this.id);
		if (loaded_state !== null) {
			state = (loaded_state == 'open') ?
				Mozilla.Expander.OPEN : Mozilla.Expander.CLOSED;
		}
	}
	return state;
}

Mozilla.Expander.prototype.toggleWithAnimation = function(e)
{
	if (this.state == Mozilla.Expander.OPEN) {
		this.closeWithAnimation();
	} else {
		this.openWithAnimation();
	}
}

Mozilla.Expander.prototype.openWithAnimation = function()
{
	if (this.semaphore)
		return;

	YAHOO.util.Dom.removeClass(this.container, 'expander-closed');
	YAHOO.util.Dom.addClass(this.container,    'expander-open');

	// get display height
	this.content_animation.parentNode.style.overflow = 'hidden';
	this.content_animation.parentNode.style.height = '0';
	this.content_animation.style.visibility = 'hidden';
	this.content_animation.style.overflow = 'hidden';
	this.content_animation.style.display = 'block';
	this.content_animation.style.height = 'auto';
	var height = this.content_animation.offsetHeight;
	this.content_animation.style.height = '0';
	this.content_animation.style.visibility = 'visible';
	this.content_animation.parentNode.style.height = '';
	this.content_animation.parentNode.style.overflow = 'visible';

	var attributes = { height: { to: height, from: 0 } };
	var animation = new YAHOO.util.Anim(this.content_animation, attributes,
		Mozilla.Expander.OPEN_DURATION,
		YAHOO.util.Easing.easeOut);

	this.semaphore = true;
	animation.onComplete.subscribe(this.handleOpen, this, true);
	animation.animate();

	this.state = Mozilla.Expander.OPEN;
}

Mozilla.Expander.prototype.closeWithAnimation = function()
{
	if (this.semaphore)
		return;

	this.content_animation.style.overflow = 'hidden';
	this.content_animation.style.height = 'auto';
	var attributes = { height: { to: 0 } };
	var animation = new YAHOO.util.Anim(this.content_animation, attributes,
		Mozilla.Expander.CLOSE_DURATION,
		YAHOO.util.Easing.easeOut);

	this.semaphore = true;
	animation.onComplete.subscribe(this.handleClose, this, true);
	animation.animate();

	this.state = Mozilla.Expander.CLOSED;
}

Mozilla.Expander.prototype.open = function()
{
	YAHOO.util.Dom.removeClass(this.container, 'expander-closed');
	YAHOO.util.Dom.addClass(this.container, 'expander-open');

	this.semaphore = false;

	this.state = Mozilla.Expander.OPEN;
	this.anchor.title = Mozilla.Expander.CLOSE_TEXT;

	this.saveState();
}

Mozilla.Expander.prototype.close = function()
{
	YAHOO.util.Dom.removeClass(this.container, 'expander-opened');
	YAHOO.util.Dom.addClass(this.container,    'expander-closed');

	this.semaphore = false;

	this.state = Mozilla.Expander.CLOSED;
	this.anchor.title = Mozilla.Expander.OPEN_TEXT;

	this.saveState();
}

Mozilla.Expander.prototype.handleOpen = function()
{
	// allow font resizing to work again
	this.content_animation.style.height = 'auto';

	// re-set overflow to visible for styles that might depend on it
	this.content_animation.style.overflow = 'visible';

	this.anchor.title = Mozilla.Expander.CLOSE_TEXT;

	this.semaphore = false;

	this.saveState();
}

Mozilla.Expander.prototype.handleClose = function()
{
	YAHOO.util.Dom.removeClass(this.container, 'expander-open');
	YAHOO.util.Dom.addClass(this.container, 'expander-closed');

	this.anchor.title = Mozilla.Expander.OPEN_TEXT;

	this.semaphore = false;

	this.saveState();
}

Initial URL


Initial Description


Initial Title
Mozilla expander

Initial Tags
javascript, textmate

Initial Language
Other