// DEPENDENCIES: loadEvents.js, dynamicCSS.js, prototype.js, scriptaculous.js

// global variables
var active_section_id = null;

// establish browser type
var agt = navigator.userAgent.toLowerCase();
var is_ie = ((agt.indexOf("msie") != -1) && (agt.indexOf("opera") == -1));

// set all .dhtmlStartVisible elements to display, and all .dhtmlStartInvisible to not display
createStyleRule('.dhtmlStartVisible', 'display: block');
createStyleRule('.dhtmlStartInvisible', 'display: none');
function dhtml_init() {
	// for IE5/Mac, we set individual .dhtmlStartVisible elements to display
	setElementStyleByClassName('dhtmlStartVisible', 'display', 'block');

	// now that document is built, set .dhtmlStartInvisible elements to display:none individually
	setElementStyleByClassName('dhtmlStartInvisible', 'display', 'none');

	// and with that done, we can now overwrite the .dhtmlStartInvisible style rule
	// (which we do in order to selectively display any elements thus hidden that we wish to later)
	createStyleRule('.dhtmlStartInvisible', 'display: block');
}
addDOMLoadEvent(dhtml_init);

// pre-load rollover images
var image_path = 'image/';
var loaded_images = new Array;
// jpgs
[
	'heading_about_dim', 'heading_about_lit',
	'heading_tools_dim', 'heading_tools_lit',
	'heading_blog_dim', 'heading_blog_lit',
	'heading_portfolio_dim', 'heading_portfolio_lit'
].each(
	function(index) {
		loaded_images[index] = new Image;
		loaded_images[index].src = image_path + index + '.jpg';
	}
);
// gifs
[
	'button_search_dim', 'button_search_lit', 'button_search_focus',
	'button_portfolio_previous_dim', 'button_portfolio_previous_lit', 'button_portfolio_previous_inactive',
	'button_portfolio_next_dim', 'button_portfolio_next_lit', 'button_portfolio_next_inactive'
	
].each(
	function(index) {
		loaded_images[index] = new Image;
		loaded_images[index].src = image_path + index + '.gif';
	}
);
var active_search_button = new Image;
active_search_button.src = 'image/button_search_focus.gif';
var inactive_portfolio_previous = new Image;
inactive_portfolio_previous.src = 'image/button_portfolio_previous_inactive.gif';
var inactive_portfolio_next = new Image;
inactive_portfolio_previous.src = 'image/button_portfolio_next_inactive.gif';


// functions for image lighting/dimming
function switch_image(img, index) {
	new_image = loaded_images[index];
	if (undefined != new_image) {
		img.src = new_image.src;
	}
}
function light_image(img, index) {
	switch_image(img, index + '_lit');
}
function dim_image(img, index) {
	switch_image(img, index + '_dim');
}

// functions for event listeners
function remove_listener(listener) {
	// usage: array_of_listener-data_objects.each(remove_listener)
	Event.stopObserving(listener.element, listener.eventName, listener.handler);
}


// functions for section headings
function make_heading_clickable(div) {
	// wrap <a href="#" class="script_generated" onclick="return mahzeh_activate(this);"> around div
	var section_id = $(div).up('.section').id;
	var replacement = Builder.node(
		'a',
		{
			href: '#',
			onmouseover: 'mouseover_heading(this);',
			onmouseout: 'mouseout_heading(this);',
			onclick: "return toggle_section('" + section_id + "');"
		}
	);
	replacement.className = 'script_generated';

	// clone clickable element (w/descendents) into <a>
	replacement.appendChild( div.cloneNode(true) );

	// swap replacement element into document
	div.parentNode.replaceChild(replacement, div);

	return replacement;
}
function unmake_heading_clickable(element) {
	// remove <a class="script_generated"> from around element
	if ( $(element).parentNode.hasClassName('script_generated') ) {	// only if it's there to be removed
		element.parentNode.parentNode.replaceChild( element, element.parentNode );
	}
}
function mouseover_heading(a) {
	// light up heading image within given <a>
	var heading = $(a).down('.heading');
	var heading_image = heading.down('.heading_image');
	light_image(heading_image, heading.id);
}
function mouseout_heading(a) {
	// dim heading image within given <a>
	var section = $(a).up('.section');
	if (section.id == active_section_id) { return; }	// don't dim current section's heading
	var heading = $(a).down('.heading');
	var heading_image = heading.down('.heading_image');
	dim_image(heading_image, heading.id);
}
function headings_init() {
	// set up DHTML section headings

	// set active section
	var active_section = $$('.dhtmlDefaultSection')[0];
	var active_section_heading;
	if (undefined != active_section) {
		active_section_id = active_section.id;
		active_section_heading = active_section.down('.main').down('.heading');
	}

	// make .dhtmlClickable elements clickable and dim them
	$$('.dhtmlClickable').each(
		function(heading) {
			if (active_section_heading != heading) {	// don't dim active section's heading
				var heading_image = heading.down('.heading_image');
				dim_image(heading_image, heading.id);
			}
			heading = make_heading_clickable(heading);
		}
	);
}
addDOMLoadEvent(headings_init);


// functions for sections
var active_section_listeners = [];
var allow_section_toggle = true;
function enable_section_toggle() {
	allow_section_toggle = true;
}
function close_active_section() {
	var section = $(active_section_id);
	var meta = section.down('.meta');
	var main_content = section.down('.main').down('.content');

	// first, strip event listeners from section search form(s)
	active_section_listeners.each(remove_listener);
	active_section_listeners = [];

	// these script.aculo.us effects require IDs and inner wrappers
	if (undefined != meta) {
		meta = add_inner_div(meta);
		assign_id(meta);
	}
	if (undefined != main_content) {
		main_content = add_inner_div(main_content);
		assign_id(main_content);
	}

	// block section toggling until effects are finished
	allow_section_toggle = false;

	// start effects
	if (undefined != meta) {
		Effect.BlindUp(meta.id, { duration: 0.5 });
	}
	if (undefined != main_content) {
		Effect.SlideUp(main_content.id, { duration: 0.5, afterFinish: open_active_section });
	}
}
function open_active_section() {
	enable_section_toggle();	// in case we're being called by close_active_section()'s effects

	var section = $(active_section_id);
	if (undefined == section) { return; }

	var meta = section.down('.meta');
	var main_content = section.down('.main').down('.content');

	// these script.aculo.us effects require IDs and inner wrappers
	if (meta) {
		meta = add_inner_div(meta);
		assign_id(meta);
	}
	if (main_content) {
		main_content = add_inner_div(main_content);
		assign_id(main_content);
	}

	// attach event listeners to dhtml elements in the section
	add_search_form_listeners(section);

	// block section toggling until effects are finished
	allow_section_toggle = false;

	// start effects
	if (meta) {
		Effect.BlindDown(meta.id, {afterFinish: enable_section_toggle});
	}
	if (main_content) {
		Effect.SlideDown(main_content.id, {afterFinish: enable_section_toggle});
	}
}
function toggle_section(id) {
	// toggle active state of section whose heading was clicked
	// close currently active section if necessary

	if (!allow_section_toggle) { return; }

	kill_splashbox();	// make sure #splashbox is gone first

	if (null != active_section_id) {	// there is an active section to toggle off
		// dim old active section's heading
		var old_active_heading = $(active_section_id).down('.main').down('.heading');
		var old_active_heading_image = old_active_heading.down('.heading_image');
		dim_image(old_active_heading_image, old_active_heading.id);

		// close old active section
		close_active_section();
	}

	if (id == active_section_id) {	// we aren't toggling a new section on
		active_section_id = null;
		return false;	// burst event bubble
	}

	// open new active section now IF we don't have to close the old one first
	if (null == active_section_id) {
		active_section_id = id;
		open_active_section();
	}

	// set new active section
	active_section_id = id;

	// light new active section's heading
	if (undefined != id) {
		var new_active_heading = $(id).down('.main').down('.heading');
		var new_active_heading_image = new_active_heading.down('.heading_image');
		light_image(new_active_heading_image, new_active_heading.id);
	}

	return false;	// burst event bubble
}
function sections_init() {
	// sections have a fixed order in dhtml presentation, but may appear in any order
	// in the xhtml document structure, so we reorder them in the DOM now

	var section_sequence = ['section_blog', 'section_tools', 'section_portfolio', 'section_about', 'section_footnote'];
	var container = $('page_sections');
	var footnote = container.down('#section_footnote');

	// arrange sections in sequence, making each one's header visible
	section_sequence.each(
		function(section_id) {
			var section = $(section_id);
			if (undefined == section) { return; }

			// remove section from current position
			section.remove();

			// reinsert section at bottom of stack
			container.appendChild(section);

			// set to display
			if (section_id != active_section_id) {	// unless it's the active section
				var clickable_header = section.down('.dhtmlClickable');
				if (undefined != clickable_header) {	// or unless it doesn't have a clickable header
					section.down('.main').down('.content').setStyle({display:'none'});
					section.down('.meta').setStyle({display:'none'});
					section.setStyle({display:'block'});
				}
			}
		}
	);

	// IE fixes
	if (is_ie) {
		// this extra markup and style is required to keep IE from puking on certain effects later
		var footer = $('footer');
		if (undefined == footer) { return; }
		new Insertion.Before(footer, '<div style="position: relative; clear: both;">&nbsp;</div>');
		footer.setStyle({position:'relative', clear:'both'});
	}
}
addDOMLoadEvent(sections_init);


// functions for forms
var search_form_focus = [];
function focus_search_text(e) {
	var search_text = Event.element(e);
	var search_form = search_text.up('.search_form');

	// make a note of it
	search_form_focus[search_form.id] = true;

	// set search_text field to active style
	search_text.addClassName('search_text_active');

	// set related search_button to active image
	switch_image(search_form.down('.search_button'), 'button_search_focus');
}
function blur_search_text(e) {
	var search_text = Event.element(e);
	var search_form = search_text.up('.search_form');

	// make a note of it
	search_form_focus[search_form.id] = false;

	// set search_text field back to default (inactive) style
	search_text.removeClassName('search_text_active');

	// set related search_button back to default (dim) image
	dim_image(
		search_text.up('.search_form').down('.search_button'),
		'button_search'
	);
}
function focus_form_element(e) {
//alert('focus_form_element('+e+')');
	var element = Event.element(e);
	element.addClassName('active');
}
function blur_form_element(e) {
	var element = Event.element(e);
	element.removeClassName('active');
}
function mouseover_search_button() {
	light_image(this, 'button_search');
}
function mouseout_search_button() {
	if (search_form_focus[$(this).up('.search_form').id]) {	// search_text field is active (has focus)
		switch_image(this, 'button_search_focus');
	} else {								// search_text field is inactive (blurred)
		dim_image(this, 'button_search');
	}
}
function add_search_form_listeners(section) {
	// attach event listeners to search form elements in this section
	$(section).getElementsBySelector('.search_form').each(
		function(form) {
			var handler;
			var search_text = form.down('.search_text');

			// attach focus handler to text field
			handler = focus_search_text.bindAsEventListener(search_text);
			Event.observe(search_text, 'focus', handler);
			active_section_listeners.push({ element: search_text, eventName: 'focus', handler: handler });

			// attach blur handler to text field
			handler = blur_search_text.bindAsEventListener(search_text);
			Event.observe(search_text, 'blur', handler);
			active_section_listeners.push({ element: search_text, eventName: 'blur', handler: handler });

			// turn off text field autocompletion (which causes problems in Mozilla)
			search_text.setAttribute('autocomplete', 'off');

			// attach mouseover handler to img button
			var search_button = form.down('.search_button');
			handler = mouseover_search_button.bindAsEventListener(search_button);
			Event.observe(search_button, 'mouseover', handler);
			active_section_listeners.push({ element: search_button, eventName: 'mouseover', handler: handler });

			// attach mouseout handler to img button
			handler = mouseout_search_button.bindAsEventListener(search_button);
			Event.observe(search_button, 'mouseout', handler);
			active_section_listeners.push({ element: search_button, eventName: 'mouseout', handler: handler });

			// dim img button
			dim_image(search_button, 'button_search');
		}
	);
}
function yomama(element) {
	alert('yomama: ' + element);
}
function forms_init() {
	// initialize any dhtml elements of a search form that is open by default
	var active_section = $(active_section_id);
	if (undefined != active_section) {
		add_search_form_listeners(active_section);
	}

	// initialize dhtml elements of the comments form if it's around
	var comments_form = $('comments_form');
	if (undefined != comments_form) {
		comments_form.getElementsBySelector('input', 'textarea').each(
			function(element) {
				// attach focus/blur handlers
				Event.observe(element, 'focus', focus_form_element.bindAsEventListener(element));
				Event.observe(element, 'blur', blur_form_element.bindAsEventListener(element));

				// turn off text field autocompletion (which causes problems in Mozilla)
				element.setAttribute('autocomplete', 'off');
			}
		);
	}
}
addDOMLoadEvent(forms_init);


// functions for portfolio browsing
var active_portfolio_index = null;
var portfolio_ids = [];
var mouse_is_over = { portfolio_previous: false, portfolio_next: false };
var portfolio_next_flash_intervalID;
var portfolio_rewind_step_duration = null;
function control_is_active(control) {
	return $(control).hasClassName('active');
}
function mouseover_portfolio_control(control) {
	mouse_is_over[control.id] = true;
	if (!control_is_active(control)) { return; }
	light_image($(control).down('img'), 'button_' + control.id);
}
function mouseout_portfolio_control(control) {
	mouse_is_over[control.id] = false;
	if (!control_is_active(control)) { return; }
	dim_image($(control).down('img'), 'button_' + control.id);
}
function portfolio_rewind() {
	// abort if already on first item
	if (! active_portfolio_index > 0) { return false; }

	// deactivate controls for the moment
	deactivate_portfolio_controls();

	// set initial step duration
	portfolio_rewind_step_duration = 0.25;

	// begin rewind chain
	portfolio_rewind_step();
}
function portfolio_rewind_step() {
	if (! active_portfolio_index > 0) {
		// if on first item, finish up
		portfolio_flip_afterFinish();
		return;
	} else {
		// otherwise, rewind one step (see portfolio_previous for detailed comments on the following)
		Effect.BlindUp(portfolio_ids[active_portfolio_index], { duration: portfolio_rewind_step_duration, fps: 32 });
		active_portfolio_index--;
		var restoreAfterFinish = !is_ie;	// restoreAfterFinish will cause IE to crash here
		Effect.SlideDown(portfolio_ids[active_portfolio_index], { duration: portfolio_rewind_step_duration, fps: 32, afterFinish: portfolio_rewind_step, restoreAfterFinish: restoreAfterFinish });
	}
	// shorten step duration
	portfolio_rewind_step_duration = portfolio_rewind_step_duration * 0.9;
}
function portfolio_previous() {
	// take focus away from clicked button
	$('portfolio_previous').blur();

	// abort if button is inactive
	if ( !control_is_active('portfolio_previous') ) { return false; }

	// deactivate controls for the moment
	deactivate_portfolio_controls();

	// roll up current entry
	Effect.BlindUp(portfolio_ids[active_portfolio_index], { duration: 0.5, fps: 32 });

	// set next to current and unfurl
	active_portfolio_index--;
	var restoreAfterFinish = !is_ie;	// restoreAfterFinish will cause IE to crash here
	Effect.SlideDown(portfolio_ids[active_portfolio_index], { duration: 0.5, fps: 32, afterFinish: portfolio_flip_afterFinish, restoreAfterFinish: restoreAfterFinish });

	// burst event bubble
	return false;
}
function portfolio_next() {
	// take focus away from clicked button
	$('portfolio_next').blur();

	// abort if button is inactive
	if ( !control_is_active('portfolio_next') ) { return false; }

	// make sure button isn't flashing
	if (portfolio_next_flash_intervalID != undefined) { portfolio_next_flash_stop(); }

	// deactivate controls for the moment
	deactivate_portfolio_controls();

	// roll up current entry
	Effect.SlideUp(portfolio_ids[active_portfolio_index], { duration: 0.5, fps: 32 });

	// set next to current and unfurl
	active_portfolio_index++;
	Effect.BlindDown(portfolio_ids[active_portfolio_index], { duration: 0.5, fps: 32, afterFinish: portfolio_flip_afterFinish });

	// burst event bubble
	return false;
}
function portfolio_flip_afterFinish() {
	// reactivate the controls
	activate_portfolio_controls();

	// for IE, try to keep images from disappearing
	if (!is_ie) { return; }
	$(portfolio_ids[active_portfolio_index]).getElementsBySelector('img').each(
		function(img) {
			// why on earth this works is a mystery to me
			var height = img.getHeight();
			img.setStyle({height: height+'px'});
		}
	);
}
function activate_portfolio_controls() {
	var portfolio_previous = $('portfolio_previous');
	var portfolio_next = $('portfolio_next');

	// if there are previous entries, activate previous control
	if (active_portfolio_index > 0) {
		if ( portfolio_previous.hasClassName('inactive') )  { portfolio_previous.removeClassName('inactive'); }
		if ( !portfolio_previous.hasClassName('active') )  { portfolio_previous.addClassName('active'); }
		if (mouse_is_over['portfolio_previous']) {
			light_image(portfolio_previous.down('img'), 'button_portfolio_previous');
		} else {
			dim_image(portfolio_previous.down('img'), 'button_portfolio_previous');
		}
	}

	// if there are next entries, activate next control
	if (active_portfolio_index+1 < portfolio_ids.length) {
		if ( portfolio_next.hasClassName('inactive') )  { portfolio_next.removeClassName('inactive'); }
		if ( !portfolio_next.hasClassName('active') )  { portfolio_next.addClassName('active'); }
		if (mouse_is_over['portfolio_next']) {
			light_image(portfolio_next.down('img'), 'button_portfolio_next');
		} else {
			dim_image(portfolio_next.down('img'), 'button_portfolio_next');
		}
	}

	// if this is the first entry, show "arrows" navigation guide
	if (portfolio_ids.length > 1  &&  0 == active_portfolio_index) {
		$('portfolio_navigation').update('<a class="pn_scroll" href="#" onclick="portfolio_next_flash(); return false;">Click arrows to view more slides.</a>');
	}

	// if this is the last entry, show "rewind" navigation guide
	if (portfolio_ids.length > 1  &&  portfolio_ids.length-1 == active_portfolio_index) {
		$('portfolio_navigation').update('<a class="pn_rewind" href="#" onclick="portfolio_rewind(); return false;">Rewind.</a>');
	}
}
function deactivate_portfolio_controls() {
	$('portfolio_previous', 'portfolio_next').each(
		function(control) {
			if ( control.hasClassName('active') )  { control.removeClassName('active'); }
			if ( !control.hasClassName('inactive') )  { control.addClassName('inactive'); }
			switch_image(control.down('img'), 'button_' + control.id + '_inactive');
		}
	);

	// clear any #portfolio_navigation content as well
	$('portfolio_navigation').update('').className = '';
}
function portfolio_init() {
	// if portfolio entry items are visible, set up dhtml viewer

	// find portfolio items, if any
	var portfolio_items = $$('.portfolio.item');
	if (0 == portfolio_items.length) { return; }

	// prepare each portfolio item, finding greatest height and currently displayed
	var tallest = 0;
	active_portfolio_index = 0;
	portfolio_items.each(
		function(item) {
			// is currently displayed?
			if (!item.hasClassName('dhtmlStartInvisible')) {
				active_portfolio_index = portfolio_ids.length;	// if we do this BEFORE adding the id to portfolio_ids, then portfolio_ids.length is the correct index
			}

			// record id
			portfolio_ids.push(item.id);

			// take height
			var item_height = item.getHeight();
			if (item_height > tallest) { tallest = item_height; }
			// if in IE, add a clearing element to prevent display bugs
			if (is_ie) {
				new Insertion.Bottom(item.down('.body'), '<div class="ie_container_buffer"></div>');
			}

			// add inner div for slide effects
			add_inner_div(item);
		}
	);

	// insert next and previous controls and navigation guide
	var viewing_area = $('portfolio_items');
	new Insertion.Top(viewing_area, '<div class="portfolio_control" style="margin-top:10px;"><a class="inactive" id="portfolio_previous" href="#" onclick="return portfolio_previous();" onmouseover="mouseover_portfolio_control(this);" onmouseout="mouseout_portfolio_control(this);"><img src="image/button_portfolio_previous_inactive.gif" alt="Previous Project" /></a></div>');
	new Insertion.Bottom(viewing_area, '<div class="portfolio_control"><a class="inactive" id="portfolio_next" href="#" onclick="return portfolio_next();" onmouseover="mouseover_portfolio_control(this);" onmouseout="mouseout_portfolio_control(this);"><img src="image/button_portfolio_next_inactive.gif" alt="Next Project" /></a></div>');
	new Insertion.After(viewing_area, '<p id="portfolio_navigation"></p>');

	// calculate and fix maximum height needed for viewing area
 	if (tallest > 0  &&  portfolio_ids.length > 1) {
		var outer_height = viewing_area.getHeight() - $(portfolio_ids[active_portfolio_index]).getHeight();
		var max_height = outer_height + tallest;
		if (is_ie) { max_height += 15; }
		viewing_area.setStyle({height: max_height+'px'});
	}

	// activate controls
	activate_portfolio_controls();
}
function portfolio_next_flash_off() {
//	dim_image($('portfolio_next').down('img'), 'button_portfolio_next');
	dim_image($('portfolio_next').down('img'), 'button_portfolio_next');
}
function portfolio_next_flash_on() {
	switch_image($('portfolio_next').down('img'), 'button_portfolio_next_inactive');
	setTimeout(portfolio_next_flash_off, 150);
}
function portfolio_next_flash_stop() {
	clearInterval(portfolio_next_flash_intervalID);
	portfolio_next_flash_intervalID = undefined;
	activate_portfolio_controls();
}
function portfolio_next_flash() {
	if (active_portfolio_index+1 >= portfolio_ids.length) { return; }
	portfolio_next_flash_intervalID = setInterval(portfolio_next_flash_on, 250);
	setTimeout(portfolio_next_flash_stop, 1500);
}
addDOMLoadEvent(portfolio_init);
addEvent(window, 'load', portfolio_next_flash);


// functions for #splashbox
function show_splashbox() {
	// conjure the splashbox from thin air
	// note: this has to run *after* sections_init

	// get the container
	var container = $('section_about').down('.main').down('.heading');
	if (undefined == container) { return; }

	// make the container positioned
	container.setStyle({position: 'relative'});

	// insert splashbox html
	new Insertion.Top(
		container,
		'<div id="splashbox"></div>'
	);
}
function kill_splashbox() {
	var splashbox = $('splashbox');
	if (undefined == splashbox) { return; }

	var container = splashbox.parentNode;

	// remove splashbox from document
	splashbox.remove();

	// reset style of container
	container.setStyle({});
}


// script.aculo.us helper functions
var global_id_index = 1;
function generate_id() {
	return 'generated_id_' + global_id_index++;
}
function assign_id(element) {
	if ('' != element.id) { return; }	// but not if element already has an id
	element.id = generate_id();
}
function add_inner_div(original_div, onlyOnce) {
   // slips an extra plain div around the complete interior of the given div
   // (some script.aculo.us effects require this extra markup)

   // parameter default
   if (undefined === onlyOnce) { onlyOnce = true; }

   if (onlyOnce) {
      // make sure we haven't already done this
      if ( !(undefined === $(original_div).down('div.script_generated', 0)) ) {
         return original_div;
      }
   }

   // clone original (inner) div, w/o descendents, to make the new (outer) div
   var new_div = original_div.cloneNode(false);

   // strip attributes from original (inner) div
   var attrs = original_div.attributes;
   for (var i=attrs.length-1; i>=0; i--) {
      original_div.removeAttribute(attrs[i].name);
   }

   // brand inner div as .script_generated
   original_div.className = 'script_generated';

   // swap new outer div with original in document
   original_div.parentNode.replaceChild(new_div, original_div);

   // put new inner div in new outer div
   new_div.appendChild(original_div);

   return new_div;
}

// functions for popup context balloons
var inflated_element = null;	// element whose popup context item ("balloon") is open
function balloon(el, id, up_or_down) {
	// toggle balloon

	// default value
	if (undefined == up_or_down) { up_or_down = 'down'; }

	// is a balloon currently inflated?
	if (null != inflated_element) {
		// if so, is it this element's or another one's?
		if (el == inflated_element) {
			// deflate this element's balloon
			deflate();
		} else {
			// deflate that one's balloon and inflate this one's
			// (actually, with the whole-document onmousedown event, this should never happen)
			destroyBalloon();
			inflate(el, id, up_or_down);
		}
	} else {
		// inflate this element's balloon
		inflate(el, id, up_or_down);
	}

	// burst event bubble
	return false;
}
function attachBalloonStem() {
	new Insertion.Bottom('balloon', '<div class="cosmetic" id="balloon_stem"></div>');
}
var iaz_preserved_elements = [];
var iaz_preserved_zindexes = [];
function ie_apply_zindex(element_id, zindex, context_id) {
	// default values
	if (undefined == zindex) { zindex = 1; }
	var context = (undefined == context_id ? $(context_id) : $(document.body));
	var element = $(element_id);

	// undo past ie_apply_zindex()
	for (i = iaz_preserved_elements.length-1; i >= 0; i--) {
		iaz_preserved_elements[i].setStyle({'z-index': iaz_preserved_zindexes[i]});
	}
	iaz_preserved_elements = [];
	iaz_preserved_zindexes = [];

	// find relative-positioned ancestors of element within context
	element.ancestors().each(
		function(ancestor) {
			if ('relative' == ancestor.getStyle('position')) {
				// preserve ancestor's current z-index
				iaz_preserved_elements.push(ancestor);
				iaz_preserved_zindexes.push( ancestor.getStyle('z-index') );

				// apply z-index to ancestor
				ancestor.setStyle({'z-index': zindex});
			}
			if (ancestor == context) { throw $break; }
		}
	);
}
function inflate(el, id, up_or_down) {
	// is there already a balloon inflated?
	if ( null != $('balloon') ) {
		// if so, then never mind
		return;
	}

	// where will the balloon go?
	var el_pos = Position.positionedOffset(el);	// element's position X,Y in pixels
	var el_width = $(el).getWidth();			// element's width in pixels
	var el_height = $(el).getHeight();			// element's height in pixels
	var balloon_posX = el_pos[0] - 220 + Math.floor(el_width / 2);	// balloon's X position
	var balloon_posY;
	if ('down'==up_or_down) {							// balloon's Y position
		// position from container top for a 'down' balloon
		balloon_posY = el_pos[1] + 48 + el_height;
	} else {
		// position from container bottom for an 'up' balloon
		balloon_posY = Position.offsetParent(el).getHeight()-el_pos[1] + 48;
		if (is_ie) { balloon_posY = balloon_posY + el_height; }
	}

	// build new balloon
	var container = Position.offsetParent(el);	// the balloon's containing/positioning block
	var content = $('about_' + id).cloneNode(true);	// the balloon's specific content
	content.id = '';	// let's not duplicate ids if we can avoid it
	var balloon_class = up_or_down;
	var balloon_style = 
//		'z-index:1000; position:absolute; ' +
		'left:'+balloon_posX+'px; ' +
		('down'==up_or_down ? 'top:' : 'bottom:') + balloon_posY+'px; ' +
		'display:none;'
	;
	new Insertion.Bottom(container, '<div id="balloon" class="'+balloon_class+'" style="'+balloon_style+'"></div>');
	var balloon = $('balloon');				// the whole balloon (empty and stem-less for the moment)

	balloon.insertBefore(content, balloon.firstChild);
	var head = balloon.down('.head');			// the balloon content's head (to which we would like to add a closebox)
	if (null != head) {
		new Insertion.Top(head, '<a class="closebox" href="#" onclick="return deflate();">X</a>');
	}

	// make sure balloon displays on top in IE
//	if (is_ie) { ie_apply_zindex(balloon, 1000); }
ie_apply_zindex(balloon, 1000);

	// appear new balloon and attach its stem
	if (is_ie) {
		// if we're in IE, attach stem after (because the animation is ugly otherwise)
		Effect.Appear('balloon', { fps: 24, duration: 0.35, afterFinish: attachBalloonStem });
	} else {
		// otherwise, attach the balloon stem first
		Effect.Appear('balloon', { fps: 24, duration: 0.35, beforeStart: attachBalloonStem });
	}

	// now this element's balloon is inflated
	inflated_element = el;
}
function destroyBalloon() {
	Element.remove('balloon');

	// now no element's balloon is inflated
	inflated_element = null;
}
function detatchBalloonStem() {
	$('balloon').down('#balloon_stem').remove();
}
function deflate() {
	var balloon = $('balloon');

	// is there even a balloon inflated?
	if (null == balloon) {
		// if not, then never mind
		return;
	}

	// disappear balloon (and then destroy)
	Effect.Fade(
		'balloon',
		{
			fps: 18,
			duration: 0.35,
			beforeStart: detatchBalloonStem,
			afterFinish: destroyBalloon
		}
	);

	// burst event bubble
	return false;
}
function flash_mahzeh_balloon() {
	// write flash balloon content
	new Insertion.Bottom(
		document.body,
		'<div style="display: none;"><div class="item" id="about_flash_mahzeh"><div class="body"><p style="font-size: 2.5em; text-align: center;">click me!</p></div></div></div>'
	);

	// inflate balloon
	balloon($('mah_zeh'), 'flash_mahzeh');

	// remove now-copied content
	$('about_flash_mahzeh').remove();

	// set deflation
	setTimeout('deflate()', 1300);
}
function window_click(event) {
	// clicking on document outside open balloon should close balloon

	var balloon = $('balloon');
	if (null == balloon) {
		// if no balloon open, then never mind
		return;
	}
	if (
		! Position.within(balloon, Event.pointerX(event), Event.pointerY(event))
			&&
		! Position.within(inflated_element, Event.pointerX(event), Event.pointerY(event))
	) {
		destroyBalloon();
	}
}


// functions for comments and comments form
var toggle_in_process = false;
function toggle_beginning() {
	toggle_in_process = true;
}
function toggle_complete() {
	toggle_in_process = false;
}
function open_comments_form_complete() {
	var author_field = $('author');
	if (undefined != author_field) { author_field.focus(); }
	toggle_complete();
}
function toggle_comments() {
	if (toggle_in_process) { return false; }
	if ($('comments').visible()) {
		if (is_ie) {
			$('comments').hide();
		} else {
			Effect.Fade('comments', { beforeStart: toggle_beginning, afterFinish: toggle_complete });
		}
	} else {
		Effect.Appear('comments', { beforeStart: toggle_beginning, afterFinish: toggle_complete });
	}
	return false;
}
function toggle_comments_form() {
	if (toggle_in_process) { return false; }
	var comments_form = $('comments_form');
	if (comments_form.visible()) {
		Effect.SlideUp('comments_form', { duration: 0.5, beforeStart: toggle_beginning, afterFinish: toggle_complete });
	} else {
		add_inner_div(comments_form);
		var restoreAfterFinish = !is_ie;	// restoreAfterFinish will cause IE to crash here
		Effect.SlideDown('comments_form', { duration: 0.5, restoreAfterFinish: restoreAfterFinish, beforeStart: toggle_beginning, afterFinish: open_comments_form_complete });
	}
	return false;
}
