/**
 * @fileOverview	Classes and code run on the document.ready event.
 * @author			Dean Hall
 * @version			1.2
 */

(function($) {

 	var article_manager = nav_link_manager = audio_mood_manager = video_manager = null;

	var _debug = function(s) {
		try { console.debug(s); } catch(e) { }
	};

	var assert = function(val) {
		try {
			if (!val) throw 'EXCEPTION: assert: ' + this.caller.toString();
		}
		catch (e) { _debug('assert not supported'); }
	};

	var transform_button_rect = function($node) {
		$node.replaceWith(
			$(document.createElement('div'))
				.attr('id', $node.attr('id'))
				.addClass('button-rect')
				.append(
					$(document.createElement('div')).addClass('left')
				)
				.append(
					$(document.createElement('div'))
						.addClass('middle')
						.append($(document.createElement('a')).text($node.text()))
				)
				.append($(document.createElement('div')).addClass('right'))
		);
		return $node;
	};

	/**
	 * an enumeration of possible media states
	 */
	var media_states = {
		INACTIVE	: 1,
		ACTIVE		: 2,
		LOADING		: 3,
		PLAYING		: 4,
		PAUSED		: 5,
		ERROR		: 6
	};

	/**
	 * abstract class activation_manager
	 */
	var activation_manager_abstract_class = {
		bucket		: null,
		active_id	: '',

		/**
		 * activation_manager::init
		 */
		init : function(id) {
			_debug('activation_manager::init: ABSTRACT METHOD');
		},

		/**
		 * activation_manager::id_exists
		 */
		id_exists : function(id) {
			if (!id)				return false;
			if (!id in this.bucket)	return false;
			if (!this.bucket[id])	return false;

			return true;
		},

		/**
		 * activation_manager::activate
		 */
		activate : function(id) {
			var old_id = this.active_id;

			if (this.id_exists(id)) {
				this.active_id = id;

				if (this.bucket[id].activate)
					this.bucket[id].activate();
			}

			this.deactivate(old_id);
		},

		/**
		 * activation_manager::deactivate
		 */
		deactivate : function(id) {
			if (this.id_exists(id) && this.bucket[id].deactivate)
				this.bucket[id].deactivate();
		},

		/**
		 * activation_manager::get_current
		 */
		get_current : function() {
			if (this.active_id && this.bucket[this.active_id])
				return this.bucket[this.active_id];

			return null;
		}
	};

	/**
	 * class article_manager
	 *
	 * This instantiates article objects and
	 * dispatches relevant events to them.
	 */
	var article_manager_class = function(id) {
		this.init(id);
	};

	$.extend(
		article_manager_class.prototype,
		activation_manager_abstract_class,
		/** @lends article_manager_class.prototype */
		{
			/**
			 * article_manager::init
			 */
			init : function(id) {
				this.bucket		= { };

				// Find all article nodes and create new article objects.
				$('.article').each($.proxy(function(i, a_node) {
					var a = new article_class($(a_node));

					this.bucket[a.id] = a;

					// Activate or deactivate it depending on the initial active article.
					if (a.id == id)
						this.activate(a.id);
					else
						this.deactivate(a.id);
				}, this));

				// Listen to clicks on all nav links to show/hide articles.
				$('.nav-link').bind('click', $.proxy(this.click, this));
			},

			/**
			 * article_manager::click
			 * event-handler for nav-link click
			 */
			click : function(event) {
				assert(event.currentTarget.mlp.article_id);
				assert(this.active_id);

				var aid = event.currentTarget.mlp.article_id;

				if (this.active_id != aid)
					this.activate(aid);

				// This event should bubble.
				return true;
			}
		}
	);


	/**
	 * class nav_link_manager
	 *
	 * This instantiates nav_link objects and
	 * dispatches relevant events to them.
	 */
	var nav_link_manager_class = function(article_id) {
		this.init(article_id);
	};

	$.extend(
		nav_link_manager_class.prototype,
		activation_manager_abstract_class,
		/** @lends nav_link_manager_class.prototype */
		{
			/**
			 * nav_link_manager::init
			 */
			init : function(article_id) {
				this.bucket	= { };

				var id = article_id + '-link';

				$('.nav-link').each($.proxy(function(i, item) {
					var nl = new nav_link_class($(item));

					this.bucket[nl.id] = nl;

					if (nl.id == id)
						nl.$.click();
					else
						this.deactivate(nl.id);

					nl.$.bind('click', $.proxy(this.click, this));
				}, this));
			},

			/**
			 * nav_link_manager::click
			 * event-handler for nav_link click
			 */
			click : function(event) {
				assert(event.currentTarget.mlp.id);
				assert(this.active_id);

				var nlid = event.currentTarget.mlp.id;
				if (this.active_id != nlid)
					this.activate(nlid);

				// This event should bubble.
				return true;
			}
		}
	);


	/**
	 * class audio_mood_manager
	 *
	 * This instantiates audio_mood objects and
	 * dispatches relevant events to them.
	 */
	var audio_mood_manager_class = function(player) {
		this.init(player);
	};

	$.extend(
		audio_mood_manager_class.prototype,
		activation_manager_abstract_class,
		/** @lends {audio_mood_manager_class.prototype} */
		{
			/**
			 * audio_mood_manager::init
			 */
			init : function(player) {
				this.bucket	= { };

				$('#audio li.mood').each($.proxy(function(i, li) {
					var m = new audio_mood_class($(li), player);

					this.bucket[m.id] = m;

					m.$header.bind('click', $.proxy(this.click, this));
				}, this));
			},

			/**
			 * audio_mood_manager::click
			 * event-handler for click
			 */
			click : function(event) {
				var mood = event.currentTarget.mlp;

				if (!this.active_id || (this.active_id != mood.id))
					this.activate(mood.id);
			}
		}
	);


	/**
	 * class video_manager_class
	 *
	 * This simply instantiates video_clip objects.
	 */
	var video_manager_class = function(player) {
		this.$		= $('#video');
		this.node	= this.$.get(0);
		this.id		= this.$.attr('id');
		this.player	= player;

		this.$.find('.video').each($.proxy(function(i, li) {
			new video_clip_class(new video_button_class($(li)), this.player);
		}, this));
	};


	/**
	 * abstract class media_player
	 */
	var media_player_abstract_class = {
		$play_space		: null,
		clip			: null,
		html5			: false,
		$media			: null,
		is_paused		: false,
		is_active		: false,
		volume			: 0.6,
		$error_msg		: null,

		SPACE_ID	: '',
		HTML5_ID	: '',
		FLASH_ID	: '',

		/**
		 * media_player::init
		 */
		init : function() {
			_debug('media_player::init: ABSTRACT METHOD');
		},

		/**
		 * media_player::init_article_handler
		 */
		init_article_handler : function(article_id, event_ns) {
			var handlers = { };

			handlers['article-activating.'		+ event_ns]	= $.proxy(this.activate, this);
			handlers['article-deactivating.'	+ event_ns]	= $.proxy(this.deactivate, this);

			article_manager.bucket[article_id].$.bind(handlers);
		},

		/**
		 * media_player::init_play_space
		 */
		init_play_space : function() {
			this.$play_space =
				$(document.createElement('div'))
					.addClass('play-space')
					.attr('id', this.SPACE_ID)
					.css('display', 'none');
		},

		/**
		 * media_player::init_error_msg
		 */
		init_error_msg : function() {
			this.$error_msg =
				$(document.createElement('p'))
					.addClass('message')
					.text('Sorry! Something went wrong when trying to play this video.');
		},

		/**
		/* media_player::show
		 */
		show : function() {
			this.$play_space.show();
		},

		/**
		 * media_player::hide
		 */
		hide : function() {
			this.$play_space.hide();
		},

		/**
		 * media_player::play
		 */
		play : function() {
			if (this.$media)
				this.$media.get(0).play();

			this.is_paused = false;
		},

		/**
		 * media_player::pause
		 */
		pause : function() {
			if (this.$media)
				this.$media.get(0).pause();

			this.is_paused = true;
		},

		/**
		 * media_player::activate
		 * event-handler for article-activating
		 */
		activate : function() {
			//_debug('media_player::activate: INVOKED');
			this.is_active	= true;
			this.is_paused	= false;

			return false;
		},

		/**
		 * media_player::deactivate
		 * event-handler for article-deactivating
		 */
		deactivate : function(event_ns) {
			//_debug('media_player::deactivate');
			this.is_active	= false;
			this.is_paused	= true;

			if (!this.clip || !this.$media) return;

			var m = this.$media.get(0);

			if (m.readyState == m.HAVE_ENOUGH_DATA) {
				//_debug('media_player::deactivate: pausing immediately');
				m.pause();
			}

			return false;
		},

		/**
		 * media_player::spawn_prep
		 */
		spawn_prep : function(old_clip, new_clip) {
			_debug('media_player::spawn_prep: ABSTRACT METHOD');
		},

		/**
		 * media_player::prep_play_space
		 */
		prep_play_space : function(new_clip) {
			_debug('media_player::spawn_prep_play_space: ABSTRACT METHOD');
		},

		/**
		 * media_player::handle_clip_change
		 */
		handle_clip_change : function(new_clip) {
			_debug('media_player::handle_clip_change: ABSTRACT METHOD');
		},

		/**
		 * media_player::spawn
		 */
		spawn : function(new_clip) {
			//_debug('media_player::spawn: INVOKED');

			if (this.clip)
				this.clip.set_state(media_states.INACTIVE);

			this.handle_clip_change(new_clip);

			this.unload();
			this.$play_space.css('display', 'none');

			this.clip = new_clip;
			this.clip.set_state(media_states.ACTIVE);

			this.prep_play_space();

			if (this.html5)
				this.spawn_html();
			else
				this.spawn_flash();
		},

		/**
		 * media_player::create_html_element
		 */
		create_html_element : function() {
			this.$media =
				$(document.createElement(this.ELEMENT))
					.attr({
						title		: this.clip.button.title,
						controls	: 'controls',
						id			: this.HTML5_ID,
						autobuffer	: 'autobuffer'
					})
					.bind({
						'canplaythrough.mp' : $.proxy(function(event) {
							this.show();
							if (this.is_paused)
								this.clip.set_state(media_states.PAUSED);
							else
								this.play();
						}, this),
						'loadstart.mp' : $.proxy(function(event) {
							this.clip.set_state(media_states.LOADING);
						}, this),
						'play.mp' : $.proxy(function(event) {
							this.clip.set_state(media_states.PLAYING);
						}, this),
						'pause.mp' : $.proxy(function(event) {
							this.clip.set_state(media_states.PAUSED);
						}, this),
						'ended.mp' : $.proxy(function(event) {
							this.clip.set_state(media_states.PAUSED);
							this.hide();
						}, this)
					})
					.bind('dataunavailable.mp empty.mp error.mp', $.proxy(function(event) {
						this.trash_media();
						this.$media = null;

						this.clip.set_state(media_states.ERROR);
						this.$play_space
							.append(this.$error_msg)
							.append(this.clip.button.$fallback)
							.show();
					}, this));

			for (ext in this.clip.files) {
				//_debug('appending: ' + this.clip.files[ext].url + '; ' + this.clip.files[ext].mime_type);
				this.$media
					.append(
						$(document.createElement('source'))
							.attr({
								src		: this.clip.files[ext].url,
								type	: this.clip.files[ext].mime_type
							})
					);
			}

			this.$media
				.append(this.clip.button.$fallback)

			this.$media.get(0).volume = this.volume;
		},

		/**
		 * media_player::amend_html_element
		 */
		amend_html_element : function() {
			_debug('media_player::amend_html_element: ABSTRACT METHOD');
		},

		/**
		 * media_player::spawn_html
		 */
		spawn_html : function() {
			this.create_html_element();
			this.amend_html_element();
			this.$play_space.prepend(this.$media);
		},

		/**
		 * media_player::embed_flash
		 */
		embed_flash : function() {
			_debug('media_player::embed_flash: ABSTRACT METHOD');
		},

		/**
		 * media_player::spawn_flash
		 */
		spawn_flash : function() {
			_debug('media_player::spawn_flash: INVOKED');
			this.clip.set_state(media_states.PAUSED);

			this.$play_space
				.prepend(
					$(document.createElement('div'))
						.attr('id', this.FLASH_ID)
						.append(this.clip.button.$fallback)
				)
				.show();

			this.embed_flash();
		},

		/**
		 * media_player::unload
		 */
		unload : function() {
			//_debug('media_player::unload: INVOKED');

			this.hide();

			if (this.clip) {
				this.clip.set_state(media_states.INACTIVE);
				this.clip = null;
			}

			this.$error_msg.detach();
			this.$play_space.empty().detach();

			if (this.$media) {
				this.$media.hide().detach();
				this.trash_media();
				this.$media = null;
			}
		},

		/**
		 * media_player::trash_media
		 */
		trash_media : function() {
			if (!this.$media) return;

			this.$media
				.detach()
				.unbind('canplaythrough dataunavailable empty ended error loadstart pause play progress volumechange');

			var m = this.$media.get(0);
			m.volume = 0;

			if ((m.readyState == m.HAVE_ENOUGH_DATA) || m.error) {
				this.$media.remove();
			}
			else {
				this.$media.bind('canplaythrough dataunavailable empty error', $.proxy(function(event) {
					//_debug('media_player::trash_media: trashing media (callback)');
					this.remove();
					return false;
				}, this.$media));
			}
		},
	};


	/**
	 * class audio_player
	 */
	var audio_player_class = function() {
		this.init();
	};

	$.extend(
		audio_player_class.prototype,
		media_player_abstract_class,
		/** @lends audio_player_class.prototype */
		{
			ARTIST		: 'Mocha Lab Productions',
			SPACE_ID	: 'audio-player',
			HTML5_ID	: 'audio-html-player',
			FLASH_ID	: 'audio-flash-player',
			ELEMENT		: 'audio',
			FLASH_SWF	: 'http://cloud.foomg.com/common/flash/audio-player/2.0/player.swf',

			/**
			 * audio_player::init
			 */
			init : function() {
				this.html5 = (
					Modernizr.audio.ogg == 'probably'
						||
					Modernizr.audio.m4a == 'probably'
						||
					Modernizr.audio.mp3 == 'probably'
				);

				this.init_article_handler('audio', 'ap');
				this.init_play_space();
				this.init_error_msg();

				if (!this.html5) {
					AudioPlayer.setup(this.FLASH_SWF, {
						bg					: '999999',
						border				: 'b9b9b9',
						initialvolume		: 60,
						leftbg				: 'f5f5f5',
						lefticon			: '505050',
						loader				: '65cbfe',
						rightbg				: 'b9b9b9',
						rightbghover		: '505050',
						righticon			: '505050',
						righticonhover		: 'b9b9b9',
						track				: 'f5f5f5',
						transparentpagebg	: 'yes',
						text				: '505050',
						track				: 'f5f5f5',
						tracker				: 'b9b9b9',
						voltrack			: 'b9b9b9',
						volslider			: '505050',
						width				: 290,
					});
				}
			},

			/**
			 * audio_player::handle_mood_nav
			 * event-handler for audio_mood:mood-deactivating
			 */
			handle_mood_nav : function() {
				//_debug('audio_player::handle_mood_nav: INVOKED');

				audio_mood_manager.get_current().$.unbind('mood-deactivating');
				this.hide();

				if (this.clip)
					this.clip.set_state(media_states.INACTIVE);

				this.unload();
			},

			/**
			 * audio_player::handle_clip_change
			 */
			handle_clip_change : function(new_clip) {
				//_debug('audio_player::handle_clip_change: INVOKED');

				if (!this.clip || (this.clip.parent_mood != new_clip.parent_mood)) {
					audio_mood_manager.get_current().$
						.bind('mood-deactivating.ap', $.proxy(this.handle_mood_nav, this));
				}
			},

			/**
			 * audio_player::prep_play_space
			 */
			prep_play_space : function() {
				//_debug('audio_player::prep_play_space');
				this.clip.parent_mood.$.append(this.$play_space);
			},

			/**
			 * audio_player::amend_html_element
			 */
			amend_html_element : function() { },

			/**
			 * audio_player::embed_flash
			 */
			embed_flash : function() {
				AudioPlayer.embed(this.FLASH_ID, {
					soundFile	: this.clip.files.mp3.url,
					titles		: this.clip.button.title,
					artists		: this.ARTIST,
					autostart	: this.is_paused ? 'no' : 'yes'
				});
			}
		}
	);


	var video_player_class = function() {
		this.init();
	};

	$.extend(
		video_player_class.prototype,
		media_player_abstract_class,
		/** @lends {video_player_class.prototype} */
		{
			$parent		: null,
			$play_space	: null,

			SPACE_ID	: 'video-player',
			HTML5_ID	: 'video-html-player',
			FLASH_ID	: 'video-flash-player',
			ELEMENT		: 'video',
			FLASH_SWF	: 'http://cloud.foomg.com/common/flash/jw-media-player/5.1/player.swf',

			/**
			 * video_player::init
			 */
			init : function() {
				this.html5	= ( Modernizr.video.ogg == 'probably' || Modernizr.video.h264 == 'probably' );

				this.init_article_handler('video', 'vp');
				this.init_play_space();
				this.init_error_msg();

				this.$parent = $('#video');
			},

			/**
			 * video_player::handle_clip_change
			 */
			handle_clip_change : function(old_clip, new_clip) { },

			/**
			 * video_player::prep_play_space
			 */
			prep_play_space : function() {
				this.$parent.append(this.$play_space);
			},

			/**
			 * video_player::amend_html_element
			 */
			amend_html_element : function() {
				this.$media.attr('poster', this.clip.poster.url);
			},

			/**
			 * video_player::embed_flash
			 */
			embed_flash : function() {
				_debug('video_player::embed_flash: INVOKED');
				_debug('width: ' + this.clip.poster.width + '; height: ' + this.clip.poster.height);
				swfobject.embedSWF(
					this.FLASH_SWF, this.FLASH_ID, this.clip.poster.width, this.clip.poster.height, '9', false,
					{
						'image'		: this.clip.poster.url,
						'file'		: this.clip.files.m4v.url,
						'volume'	: '60',
						'autostart'	: this.is_paused ? 'false' : 'true'
					}
				);
			}
		}
	);


	/**
	 * class article
	 *
	 * This represents an article (div.article) element.
	 * It only manages showing and hiding itself.
	 *
	 */
	var article_class = function($article) {
		this.init($article);
	};

	$.extend(
		article_class.prototype,
		/** @lends {article_class.prototype} */
		{
			$		: null,
			node	: null,
			id		: '',

			/**
			 * article::init
			 */
			init : function($article) {
				this.$			= $article;
				this.node		= this.$.get(0);
				this.node.mlp	= this;
				this.id			= this.$.attr('id');

				this.$
					.find('h2').remove().end()
					.find('.top-link').remove();
			},

			/**
			 * article::activate
			 */
			activate : function() {
				this.$.show();
				this.$.trigger('article-activating');
			},

			/**
			 * article::deactivate
			 */
			deactivate : function() {
				this.$.hide();
				this.$.trigger('article-deactivating');
			},
		}
	);


	/**
	 * class nav_link
	 *
	 * This represents a navigation (#nav a) element.
	 * It only handles changing its appearance on clicks.
	 *
	 */
	var nav_link_class = function($link) {
		this.init($link);
	};

	$.extend(
		nav_link_class.prototype,
		/** @lends {nav_link_class.prototype} */
		{
			$			: null,
			node		: null,
			id			: '',
			$link		: null,
			item		: null,
			title		: '',
			article_id	: '',

			/**
			 * nav_link::init
			 */
			init : function($item) {
				this.$			= $item;
				this.node		= this.$.get(0);
				this.node.mlp	= this;
				this.id			= this.$.attr('id');
				this.$link		= this.$.find('a:first');
				this.href		= this.$link.attr('href');
				this.title		= this.$link.text();
				this.article_id	= this.id.replace(/-link$/, '');

				transform_button_rect(this.$link);
			},

			/**
			 * nav_link::activate
			 */
			activate : function() {
				document.location = this.href;
				this.$.addClass('active');

				$(document).find('head title:first').replaceWith(
					$(document.createElement('title')).text(this.title + ' » Mocha Lab Productions')
				);
			},

			/**
			 * nav_link::deactivate
			 */
			deactivate : function() {
				this.$.removeClass('active');
			}
		}
	);


	/**
	 * class audio_mood
	 *
	 * This represents an audio section (.mood).
	 * It only handles hiding and showing its list
	 * of clips and styling itself.
	 *
	 */
	var audio_mood_class = function($mood, player) {
		this.init($mood, player);
	};

	$.extend(
		audio_mood_class.prototype,
		/** @lends {audio_mood_class.prototype} */
		{
			$		: null,
			node	: null,
			id		: '',
			$header	: null,

			/**
			 * audio_mood::init
			 */
			init : function($mood, player) {
				this.$			= $mood;
				this.node		= this.$.get(0);
				this.node.mlp	= this;
				this.id			= this.$.attr('id');
				this.$header	= this.$.find('.header:first h3:first');

				// Give the header a reference to this object so it can be
				// referenced easily in event handlers.
				this.$header.get(0).mlp = this;

				this.$header
					.append($(document.createElement('span')).addClass('select'))
					.attr('title', 'Listen to ' + this.$header.text().toLowerCase() + ' audio samples.');

				this.$.find('.clip-list .clip').each($.proxy(function(i, li) {
					new audio_clip_class(new audio_button_class($(li)), this, player);
				}, this));
			},

			/**
			 * audio_mood::activate
			 */
			activate : function() {
				this.$.addClass('active');
			},

			/**
			 * audio_mood::deactivate
			 */
			deactivate : function() {
				this.$.removeClass('active');
				this.$.trigger('mood-deactivating');
			}
		}
	);


	/**
	 * class video_poster
	 *
	 * This represents the metadata for a video's poster image.
	 */
	var video_poster_class = function($img) {
		this.init($img);
	};

	$.extend(
		video_poster_class.prototype,
		/** @lends {video_poster_class.prototype} */
		{
			url		: '',
			height	: '',
			width	: '',

			/**
			 * video_poster::init
			 */
			init : function($img) {
				this.url	= $img.attr('src');
				this.height	= $img.attr('height')	|| $img.css('height');
				this.width	= $img.attr('width')	|| $img.css('width');
			}
		}
	);

	/**
	 * class media_file
	 *
	 * This represents the metadata for a media file.
	 */
	var media_file_class = function(url, mime_type) {
		this.init(url, mime_type);
	};

	$.extend(
		media_file_class.prototype,
		/** @lends {media_file_class.prototype} */
		{
			url			: '',
			mime_type	: '',

			/**
			 * media_file::init
			 */
			init : function(url, mime_type) {
				this.url		= url;
				this.mime_type	= mime_type;
			}
		}
	);


	/**
	 * @lends audio_clip_class.prototype
	 * @lends video_clip_class.prototype
	 */
	var media_clip_abstract_class = {
		button	: null,
		player	: null,
		state	: media_states.INACTIVE,
		files	: null,

		/**
		 * media_clip::init_button
		 */
		init_button : function(button) {
			//_debug('media_clip::init_button: INVOKED');
			this.button = button;
			this.button.$.bind('click', $.proxy(this.click, this));
		},

		/**
		 * media_clip::init_files
		 */
		init_files : function(list, selector) {
			//_debug('media_clip::init_button: INVOKED');
			this.files = { };

			for (var ext in list) {
				//_debug('media_clip::init_files: selector: ' + selector + '.' + ext + ':first');
				//_debug(this.button.$.find(selector + '.' + ext + ':first'));
				this.files[ext] = new media_file_class(this.button.$.find(selector + '.' + ext + ':first').attr('href'), list[ext]);
			}
		},

		/**
		 * media_clip::set_state
		 */
		set_state : function(state) {
			this.state = state;

			switch (state) {
				case media_states.ACTIVE :
					//_debug('media_clip[' + this.button.id + ']::set_state: state is now: ACTIVE');
					this.button.activate();
					break;
				case media_states.LOADING :
					//_debug('media_clip[' + this.button.id + ']::set_state: state is now: LOADING');
					this.button.show_loading();
					break;
				case media_states.PLAYING :
					//_debug('media_clip[' + this.button.id + ']::set_state: state is now: PLAYING');
					this.button.show_playing();
					break;
				case media_states.PAUSED :
					//_debug('media_clip[' + this.button.id + ']::set_state: state is now: PAUSED');
					this.button.show_paused();
					break;
				case media_states.INACTIVE :
					//_debug('media_clip[' + this.button.id + ']::set_state: state is now: INACTIVE');
					if (this.state != media_states.ERROR)
						this.button.deactivate();
					break;
				case media_states.ERROR :
					//_debug('media_clip[' + this.button.id + ']::set_state: state is now: ERROR');
					this.button.show_error();
					break;
				default :
					_debug('media_clip[' + this.button.id + ']::set_state: state is now: UNDEFINED');
			}
		},

		/**
		 * media_clip::click
		 * event-handler for this.button:click
		 *
		 * 'this' is this object.
		 */
		click : function(event) {
			// The player is currently loading/playing this clip; change my state.
			if (this.player.clip == this) {
				switch (this.state) {

					case media_states.PLAYING :
						//_debug('media_clip::click: pausing');
						this.player.pause();
						this.set_state(media_states.PAUSED);
						break;

					case media_states.PAUSED :
						//_debug('media_clip::click: playing');
						this.player.play();
						this.set_state(media_states.PLAYING);
						break;

					default :
						_debug('media_clip::click: ' + this.button.id + ': clicked on a non-clickable state: ' + this.state);
				}
			}
			// Tell the player to load and play this clip.
			else {
				this.player.spawn(this, this);
			}
		}
	};


	/**
	 * class audio_clip
	 *
	 */
	var audio_clip_class = function(button, parent_mood, player) {
		this.init(button, parent_mood, player);
	};

	$.extend(
		audio_clip_class.prototype,
		media_clip_abstract_class,
		/** @lends {audio_clip_class.prototype} */
		{
			parent_mood	: null,

			/**
			 * audio_clip::init
			 */
			init : function(button, parent_mood, player) {
				this.parent_mood	= parent_mood;
				this.player			= player;

				this.init_button(button);
				this.init_files({ oga : 'audio/ogg', m4a : 'audio/m4a', mp3 : 'audio/mpeg' }, 'a.audio-file');
				this.button.init_button();
			}
		}
	);


	/**
	 * class video_clip
	 *
	 */
	var video_clip_class = function(button, player) {
		this.init(button, player);
	};

	$.extend(
		video_clip_class.prototype,
		media_clip_abstract_class,
		/** @lends {video_clip_class.prototype} */
		{
			poster : null,

			/**
			 * video_clip::init
			 */
			init : function(button, player) {
				this.player	= player;

				this.init_button(button);
				this.init_files({ ogv : 'video/ogg', m4v : 'video/mp4' }, 'a.video-file');

				var $p = this.button.$.find('img:first');
				this.poster	= new video_poster_class($p);
				$p.remove();

				this.button.init_button();
			}
		}
	);

	/** 
	 * abstract class media_button
	 *
	 * @lends audio_button_class.prototype
	 * @lends video_button_class.prototype
	 */
	var media_button_abstract_class = {
		$			: null,
		node		: null,
		id			: '',
		num			: NaN,
		title		: '',
		href		: '',
		$fallback	: null,
		$link		: null,
		$num		: null,
		$activity	: null,

		/**
		 * media_button::init_node
		 */
		init_node : function($li) {
			//_debug('media_button::init_node:: INVOKED');
			this.$			= $li;
			this.node		= this.$.get(0);
			this.node.mlp	= this;
			this.id			= this.$.attr('id');
		},

		/**
		 * media_button::init_button
		 */
		init_button : function() {
			//_debug('media_button::init_button::INVOKED');
			//_debug(this.$);
			this.$num		= $(document.createElement('span')).addClass('num').text(this.num);
			this.$activity	= $(document.createElement('span')).addClass('activity');

			this.$link =
				$(document.createElement('a'))
					//.attr('href',	this.href)
					.attr('title',	this.title)
					.append(this.$num)
					.append(this.$activity);

			this.$
				.empty()
				.addClass('button')
				.append(this.$link);
		},

		/**
		 * media_button::activate
		 */
		activate : function() {
			this.$link.addClass('active');
			this.$activity.show();
		},

		/**
		 * media_button::deactivate
		 */
		deactivate : function() {
			this.$link.removeClass('active');
			this.$activity.hide().removeClass('loading playing paused error');
		},

		/**
		 * media_button::show_loading
		 */
		show_loading : function() {
			this.$activity
				.removeClass('playing paused error')
				.addClass('loading');
		},

		/**
		 * media_button::show_playing
		 */
		show_playing : function() {
			this.$activity
				.removeClass('loading paused error')
				.addClass('playing');
		},

		/**
		 * media_button::show_paused
		 */
		show_paused : function() {
			this.$activity
				.removeClass('loading playing error')
				.addClass('paused');
		},

		/**
		 * media_button::show_error
		 */
		show_error : function() {
			this.$activity
				.removeClass('loading playing paused')
				.addClass('error');
		}
	};


	/**
	 * class audio_button
	 *
	 * This represents an audio clip list item/button (li.clip).
	 * It instantiates and displays a button and provides an
	 * interface to control its appearance (through this.show_*).
	 *
	 */
	var audio_button_class = function($li) {
		this.init($li);
	};

	$.extend(
		audio_button_class.prototype,
		media_button_abstract_class,
		/** @lends {audio_button_class.prototype} */
		{
			mood	: '',

			/**
			 * audio_button::init
			 */
			init : function($li) {
				this.init_node($li);

				this.mood	= this.id.replace(/^mood-([^-]+)-\d+$/, "$1");
				this.num	= this.id.replace(/^mood-[^-]+-(\d+)$/, "$1");

				this.title =
					this.mood.replace(/^(\w).*$/, "$1").toUpperCase() +
						this.mood.replace(/^\w(\w+)$/, "$1") +
						' Sample #' + this.num;

				this.$fallback =
					$(document.createElement('div'))
						.addClass('no-js')
						.append(this.$.find('ul:first')
						.clone(false));
			},
		}
	);


	/**
	 * class video_button
	 *
	 * This represents a video clip list item/button (li.clip).
	 * It instantiates and displays a button and provides an
	 * interface to control its appearance (through this.show_*).
	 *
	 */
	var video_button_class = function($li) {
		this.init($li);
	};

	$.extend(
		video_button_class.prototype,
		media_button_abstract_class,
		/** @lends {video_button_class.prototype} */
		{
			/**
			 * video_button::init
			 */
			init : function($li) {
				//_debug('video_button::init: INVOKED');
				this.init_node($li);

				this.num	= this.id.replace(/^video-(\d+)$/, "$1");
				this.title	= "Video #" + this.num;

				this.$fallback	=
					$(document.createElement('div'))
						.addClass('no-js')
						.append(this.$.contents().clone(false));
			}
		}
	);


	$(function() {

		var aid = window.location.hash.replace(/^#/, '');
		if (!aid || (aid == 'top'))
			aid = 'about';

		article_manager		= new article_manager_class(aid);
		nav_link_manager	= new nav_link_manager_class(aid);
		audio_mood_manager	= new audio_mood_manager_class(new audio_player_class());
		video_manager		= new video_manager_class(new video_player_class());

	});
})(jQuery);
