
var suggestion_util = null;


function hasClass(element, class_name)
{
	return (
		element.className &&
		element.className.match(new RegExp('\\b' + class_name + '\\b'))
	) ? true : false;
}

ArticleController = {
	articleID: null,

	//
	// when a stretcher the first stretcher is found,
	// it should be placed here.
	//
	firstStretcher: null,

	initialize: function() {
		var oldOnLoad = (typeof window.onload == 'function') ?
			window.onload : null;

		var thisObject = this;

		window.onload = function() {

			if (oldOnLoad) { oldOnLoad(); }

			thisObject.initializeRating();
		}

		this.articleID = this.__findArticleIDInPath(window.location.pathname) ||
			this.__findArticleIDInQueryString(window.location.search);
	},

	__findArticleIDInPath: function(path) {
		var match = path.match(/article\/(\d+)\/?$/);

		if (match) { return parseInt(match[1]); }
	},

	__findArticleIDInQueryString: function(query) {
		// match article_id and article%5Fid for FOS
		var match = query.match(/\barticle(?:_|%5[Ff])id=(\d+)/);

		if (match) { return parseInt(match[1]); }
	},

	__autoIncrementValue: 0,

	//
	// if a h3.display doesn't have an id
	// give it an auto-generated one.
	//
	updateDisplayId: function(el) {
		if (!el.id) {
			el.id = '__display-auto-increment:' + (this.__autoIncrementValue++);
		}
	},


	//
	// methods and variables for rating
	//
	__ratings: {},
	__stars: [],
	__articleRating: 0,

	initializeRating: function() {

		var stars = $$('#rating li a');

		this.__stars = new Array();
			
		for (var i = 0; i < stars.length; ++i) {
			var star = {
				element: stars[i],
				selected: null,
				unselected: null
			}
			this.__stars.push(star);
		}

		var length = this.__stars.length;
		for (var i = 0; i < length; ++i) {
			this.__stars[i].selected = this.__stars.slice(0, i + 1);
			this.__stars[i].unselected = this.__stars.slice(i + 1);
		}
		
		// read the cookie
		var ratingsCache = this.__readRatingCacheCookie();

		for (var i = 0, len = ratingsCache.length; i < len; ++i) {
			var rating = ratingsCache[i];
			if (rating.id == this.articleID) {
				this.__articleRating = rating.rating;
				this.mouseoverRating();
				break;
			}
		}

	},

	mouseoverRating: function(el) {
		var star = null;
		for (var i = 0; i < this.__stars.length; ++i) {
			if (this.__stars[i].element == el) {
				star = this.__stars[i];
				break;
			}
		}

		if (star) {
			for (var i = 0; i < star.selected.length; ++i) {
				star.selected[i].element.parentNode.className = 'selected';
			}
			for (var i = 0; i < star.unselected.length; ++i) {
				star.unselected[i].element.parentNode.className = '';
			}
		}
		else {
			for (var i = 0; i < this.__stars.length; ++i) {
				this.__stars[i].element.parentNode.className = (i < this.__articleRating) ?
					'selected' : '';
			}
		}
	},

	clickRating: function(el) {

		var star = null;
		var rating = 0;

		for (var i = 0; i < this.__stars.length; ++i) {
			if (this.__stars[i].element == el) {
				star = this.__stars[i];
				rating = i + 1;
				break;
			}
		}

		if (star) {
			this.__articleRating = rating;
			this.mouseoverRating();

			// var anchor = star.element.getElementsByTagName('a');

			// todo:
			//  - perform ajax callback
			//  - cache in cookie
			new Ajax.Request(
				el.href,
				{
					method: 'post',
					parameters: {format: 'json'},
					onSuccess: function (transport, json) {
						try {
//								alert('rating: ' + rating + '\n' +
//									'json.rating: ' + json.rating + '\n' +
//									'json.id: ' + json.id);

							if (json.id && json.rating) {
								json.rating = parseInt(json.rating);
								json.id = parseInt(json.id);

								if (json.rating == rating) {
									ArticleController.cacheRating(json.id, json.rating);
								}
							}
						}
						catch(e) {
							// alert(e);
						}
					},
					onFailure: function (transport, json) {
						// 
					}
				});
		}

		return false;
	},

	cacheRating: function(id, rating) {
		// read the cookie
		var ratingsCache = this.__readRatingCacheCookie();

		// remove any previous ranking for this id
		ratingsCache = ratingsCache.reject(function(n) { return n.id == id; });

		// push this id to the top of the stack
		ratingsCache.push({id: id, rating: rating});

		// if there are more than 50 elements, pop the first
		if (ratingsCache.length > 50) { ratingsCache.shift(); }


		// write the cookie
		this.__writeRatingCacheCookie(ratingsCache);
	},

	__readRatingCacheCookie: function() {
		var cookie = Cookie.read('article_rating');
		var ratings = [];
		var splitCookie = (cookie) ? cookie.split(',') : new Array();

		for (var i = 0, len = splitCookie.length; i < len; ++i) {
			try {
				var element = splitCookie[i];
				var splitElement = element.split(':');
				ratings.push({
					id: parseInt(splitElement[0]),
					rating: parseInt(splitElement[1]) });
			}
			catch (e) {
				// just skip on failure
			}
		}

		return ratings;
	},

	__writeRatingCacheCookie: function(ratings) {
		var ratingString = new String();
		var ratingValues = [];

		for (var i = 0, len = ratings.length; i < len; ++i) {
			var rating = ratings[i];
			ratingValues.push(rating.id + ':' + rating.rating);
		}
		ratingString = ratingValues.join(',');
		Cookie.write('article_rating', ratingString);
	},

	stopPropagation: function(evt) {
		// ie doesn't seem to pass events in
		/*@cc_on if (!evt) evt = window.event; @*/
		if (evt) {
			try {
				evt.cancelBubble = true; // ie
				if (evt.preventDefault) evt.preventDefault();
				if (evt.stopPropagation) evt.stopPropagation();
			}
			catch (e) {
				// this is a nice to have feature, not a necessity
				// if it fails for whatever reason, just eat the
				// exception
			}
		}
	},
	
	decorateDefinition: function(el) {
		el = $(el); // wrap the element for IE

		var definitionContainer = this.__drawInfoBubble(el.getAttribute('alt'));
		document.body.appendChild(definitionContainer);
		
		definitionContainer.onmouseover = el.onmouseover = function() {
			var elOffset = el.cumulativeOffset();
			var elWidth = el.getWidth();
			var elHeight = el.getHeight();
			// todo: check for negative offset
			definitionContainer.style.width = '200px';
			definitionContainer.style.top = (elOffset.top + elHeight - 7) + 'px';
			definitionContainer.style.left = (elOffset.left + (elWidth / 2) - 100) + 'px';
			definitionContainer.style.visibility = 'visible';
			definitionContainer.style.display = 'block';
			return true;
		}
		definitionContainer.onmouseout = el.onmouseout = function() {
			definitionContainer.style.display = 'none';
			definitionContainer.style.visibility = 'hidden';
			return true;
		}
		
		el.onmouseout();
	},
	
	__drawInfoBubble: function (text, offset, width) {
		
		var isIE = /*@cc_on ! @*/ false; 
		
		// IE has some issues building the table dynamically, so
		// we don't do it that way for IE.
		if (isIE) {
			var infoBubble = document.createElement('div');
			infoBubble.innerHTML = '<div class="info-bubble"><table class="container"><tbody>' +
				'<tr><td class="top-left"></td>' +
					'<td>' +
						'<table class="top-container" width="100%"><tr>' +
							'<td class="top-pad">&nbsp;</td>' +
							'<td class="top-arrow"></td>' +
							'<td class="top-pad">&nbsp;</td>' +
						'</tr></table>' +
					'</td>' +
					'<td class="top-right"></td>' +
				'</tr>' +
				'<tr>' +
					'<td class="middle-left"></td>' +
					'<td class="middle">' + text.escapeHTML() + '</td>' +
					'<td class="middle-right"></td>' +
				'</tr>' +
				'<tr>' +
					'<td class="bottom-left"></td>' +
					'<td class="bottom-center"></td>' +
					'<td class="bottom-right"></td>' +
				'</tr>' +
			'</tbody></table></div>';
			return infoBubble.firstChild;
		}
		
		var infoBubble = document.createElement('div');
		infoBubble.setAttribute('class', 'info-bubble');
		var container = document.createElement('table');
		container.setAttribute('class', 'container');
		infoBubble.appendChild(container);
		var containerBody = document.createElement('tbody');
		container.appendChild(containerBody);
		
		var topRow = document.createElement('tr');
		containerBody.appendChild(topRow)
		var topLeft = document.createElement('td');
		topLeft.setAttribute('class', 'top-left');
		topRow.appendChild(topLeft);
		var topMiddle = document.createElement('td');
		topRow.appendChild(topMiddle);
		
		
		var topContainer = document.createElement('table');
		topContainer.setAttribute('class', 'top-container');
		topContainer.setAttribute('width', '100%');
		topMiddle.appendChild(topContainer);
		
		var topContainerWidth = 168;
		if (!offset) { offset = ((168 - 30) / 2).floor(); }
		
		var topContainerRow = document.createElement('tr');
		topContainer.appendChild(topContainerRow);
		var topContainerLeftPad = document.createElement('td');
		topContainerLeftPad.setAttribute('class', 'top-pad');
		topContainerLeftPad.style.width = offset + 'px';
		topContainerRow.appendChild(topContainerLeftPad);
		var topContainerArrow = document.createElement('td');
		topContainerArrow.setAttribute('class', 'top-arrow');
		topContainerRow.appendChild(topContainerArrow);
		var topContainerRightPad = document.createElement('td');
		topContainerRightPad.setAttribute('class', 'top-pad');
		topContainerRightPad.style.width = (168 - 30 - offset) + 'px';
		topContainerRow.appendChild(topContainerRightPad);
		
		var topRight = document.createElement('td');
		topRight.setAttribute('class', 'top-right');
		topRow.appendChild(topRight);
		
		var middleRow = document.createElement('tr');
		containerBody.appendChild(middleRow);
		var middleLeft = document.createElement('td');
		middleLeft.setAttribute('class', 'middle-left');
		middleRow.appendChild(middleLeft);
		var middle = document.createElement('td');
		middle.setAttribute('class', 'middle');
		middleRow.appendChild(middle);
		var text = document.createTextNode(text);
		middle.appendChild(text);
		var middleRight = document.createElement('td');
		middleRight.setAttribute('class', 'middle-right');
		middleRow.appendChild(middleRight);
		
		var bottomRow = document.createElement('tr');
		containerBody.appendChild(bottomRow);
		var bottomLeft = document.createElement('td');
		bottomLeft.setAttribute('class', 'bottom-left');
		bottomRow.appendChild(bottomLeft);
		var bottomCenter = document.createElement('td');
		bottomCenter.setAttribute('class', 'bottom-center');
		bottomRow.appendChild(bottomCenter);
		var bottomRight = document.createElement('td');
		bottomRight.setAttribute('class', 'bottom-right');
		bottomRow.appendChild(bottomRight);
		
		return infoBubble;
	}

}

ArticleController.initialize();


Behaviour.register({
	//
	// opens the print dialog
	//
	'.printarticle' : function (el)
	{
		el.onclick = function()
		{
			window.print();
			return false;
		}
	},
	//
	// these behaviours are used to add various types of
	// accordions to articles
	//
	'#article div.stretcher' : function (el)
	{
		//
		// none of the stretchers are wrapped in innoccuous divs
		// that are required by scriptaculous to function
		// properly, and weren't required by moo.fx.  lets
		// wrap everything in a div so the writers don't have
		// to go back and edit all the stretcher content...
		//
		var container = document.createElement('div');
		var container_class = document.createAttribute('class');
		container_class.nodeValue = 'stretcher-container';
		container.setAttributeNode(container_class);
		container.style.hasLayout = false;
		// container.style.zIndex = el.style.zIndex;

		while (el.firstChild)
		{
			container.appendChild(el.removeChild(el.firstChild));
		}
		el.appendChild(container);

		ArticleController.firstStretcher = null;
	},
	'#article h3.display' : function(el)
	{
		// find the next element, which should be div.stretcher
		// based on thewatchmakerproject.com/journal/329/finding-html-elements-using-javascript-nextsibling-and-previoussibling (the comments by Mark Dalgleish)
		var elem = el.nextSibling;
		while (elem.nodeType != 1/* && elem.hasClass && !elem.hasClass('stretcher')*/)
		{
			elem = elem.nextSibling;
		}


		var group_mode = 'linked';
		var node_mode = 'normal';

		if (hasClass(el.parentNode, 'all-closed'))
		{
			node_mode = 'closed';
		}
		if (hasClass(el.parentNode, 'many-open'))
		{
			group_mode = 'unlinked';
		}
		if (hasClass(el, 'open'))
		{
			node_mode = 'open';
		}

		// at this point we should have div.stretcher
		if (ArticleController.firstStretcher == null)
		{
			ArticleController.firstStretcher = el;
		}

		if (el == ArticleController.firstStretcher)
		{
			if (node_mode == 'closed')
			{
				elem.style.display = 'none';
			}
		}
		else if (node_mode != 'open')
		{
			elem.style.display = 'none';
		}

		//
		// these don't always have a unique id,
		// but they'll need to, update it here.
		//
		ArticleController.updateDisplayId(el);

		el.onclick = function()
		{
			//
			// scroll to stretcher
			//
			var scrollTo = function() {
				new Effect.ScrollTo(
					el.id,
					{duration: 0.2});
			}

			// find all 'stretchers'
			var f = $$('#article div.stretcher');

			if (group_mode == 'linked')
			{
				for (var i = 0; i < f.length; ++i)
				{
					// if the stretcher doesn't equal elem, hide it
					if (f[i] != elem && f[i].style.display != 'none')
					{
						new Effect.SlideUp(f[i], {duration: 0.5});
					}
					else if (f[i] == elem && f[i].style.display == 'none') // if it does, show it
					{
						new Effect.SlideDown(
							f[i] ,
							{duration: 0.5, afterFinish: scrollTo});
					}
				}
			}
			else if (group_mode = 'unlinked')
			{
				for (var i = 0; i < f.length; ++i)
				{
					//
					// if we're at the current stretcher.
					//
					if (f[i] == elem && f[i].style.display != 'none')
					{
						new Effect.SlideUp(f[i], {duration: 0.5});
					}
					else if (f[i] == elem && f[i].style.display == 'none') // if it does, show it
					{
						new Effect.SlideDown(
							f[i] ,
							{duration: 0.5, afterFinish: scrollTo});
					}
				}
			}
		}
	},
	'.suggestion-disclosure' : function (el)
	{
		var suggestion_container = $('suggestion-wrapper');
		suggestion_util = new FadeAndSlideBlock();
		suggestion_util.set_id(suggestion_container.id);
		suggestion_util.set_duration(0.25);

		el.onclick = function ()
		{
			suggestion_util.toggle();
		}
	},
	'#suggestion-hide' : function (el)
	{
		el.onclick = function ()
		{
			if (suggestion_util)
			{
				suggestion_util.hide();
			}
		}
	},
	'div.suggestion-wrapper #suggestion-form' : function (el)
	{
		var user_agent = navigator.userAgent || navigator.vendor ||	window.opera;

		var form_disabled = false;

		//
		// take the form's action and switch it with a
		// javascript callback to an anonymous function
		//
		var action = el.action;
		el.onsubmit = function (event)
		{
			try
			{
				if (form_disabled) { return false; }

				var status_container = $('suggestion-status');
				while (status_container.firstChild) { status_container.removeChild(status_container.firstChild); }

				//
				// todo: validate the data
				//
				var email_value = el.email.value;
				var reason_value = el.reason.value;
				var suggestion_value = el.suggestion.value;

				if (email_value.length == 0)
				{
					status_container.appendChild(document.createTextNode('Please provide a contact email address so we can follow up with you if necessary.'));
					el.email.focus();
					return false;
				}

				if (reason_value.length == 0)
				{
					status_container.appendChild(document.createTextNode('Please provide a reason for your suggestion.'));
					el.reason.focus();
					return false;
				}

				if (suggestion_value.length == 0)
				{
					status_container.appendChild(document.createTextNode('Please provide some information outlining your concern with this article.'));
					el.suggestion.focus();
					return false;
				}

				/*@cc_on
				if (!event) { event = window.event; }
				@*/
				var form_parameters = {
					article_id: el.article_id.value,
					hash: el.hash.value,
					name: el.name.value,
					secret: user_agent,
					email: el.email.value,
					reason: el.reason.value,
					suggestion: el.suggestion.value
				};

				//
				// disable the form
				//
				el.email.disabled = true;
				el.reason.disabled = true;
				el.suggestion.disabled = true;
				// el.submit.disabled = true; // IE can't disable <input type="image" />... ugh.
				form_disabled = true;


				var paragraph = document.createElement('p');
				paragraph.appendChild(document.createTextNode('Sending...'));
				status_container.appendChild(paragraph);

				new Ajax.Request(
					el.action, // use the same action the form would
					{
						method: 'post',
						parameters: form_parameters,
						onSuccess: function (transport, json)
						{
							while (status_container.firstChild) { status_container.removeChild(status_container.firstChild); }
							try
							{
								if (json)
								{
									//
									// if there were no warnings or errors
									//
									if (!(json.flash.warnings || json.flash.errors))
									{
										for (var i = 0; i < json.flash.info.length; ++i)
										{
											var paragraph = document.createElement('p');
											paragraph.appendChild(document.createTextNode(json.flash.info[i]));
											status_container.appendChild(paragraph);
										}

										//
										// todo: print a thank you message
										//       sleep for 10 seconds,
										//       enable the form
										//       hide the form
										//
										setTimeout(function () {

											suggestion_util.hide();

											//
											// reset the form
											//
											el.reason.value = '';
											el.suggestion.value = '';

											while (status_container.firstChild) { status_container.removeChild(status_container.firstChild); }

										}, 3000);
									}
									else
									{
										var warning_list = document.createElement('ul');


										if (json.flash.warnings)
										{
											for (var i = 0; i < json.flash.warnings.length; ++i)
											{
												var list_item = document.createElement('li');
												list_item.appendChild(document.createTextNode(json.flash.warnings[i]));
												warning_list.appendChild(list_item);
											}
										}
										if (json.flash.error)
										{
											for (var i = 0; i < json.flash.error.length; ++i)
											{
												var list_item = document.createElement('li');
												list_item.appendChild(document.createTextNode(json.flash.errors[i]));
												warning_list.appendChild(list_item);
											}
										}
										status_container.appendChild(warning_list);
									}
								}
								else
								{
									throw 'JSON not returned.';
								}
							}
							catch (exception)
							{
								while (status_container.firstChild) { status_container.removeChild(status_container.firstChild); }
								status_container.appendChild(document.createTextNode('caught exception: ' + exception));
							}
						},
						onFailure: function (transport)
						{
							while (status_container.firstChild) { status_container.removeChild(status_container.firstChild); }
							status_container.appendChild(document.createTextNode('An error has occured. Please try again.'));
						},
						onComplete: function (transport)
						{
							el.email.disabled = false;
							el.reason.disabled = false;
							el.suggestion.disabled = false;
							// el.submit.disabled = false; // IE doesn't support disabled on <input type="image" />
							form_disabled = false;
						}
					}
				);

			}
			catch (exception)
			{
				while (status_container.firstChild) { status_container.removeChild(status_container.firstChild); }
				status_container.appendChild(document.createTextNode('An error has occurred. Please try again later.'));
			}

			/*
			el.email.disabled = false;
			el.reason.disabled = false;
			el.suggestion.disabled = false;
			// el.submit.disabled = false; // IE doesn't support disabled on <input type="image" />
			form_disabled = false;
			*/

			return false;
		}

	},
	
	
	'span.def' : function (el)
	{
		ArticleController.decorateDefinition(el);
	},

	'#rating' : function(el) {
		// the rating div is hidden by default so that
		// non JS browsers don't see it.
		if (el.style.display != 'block') { el.style.display = 'block'; }
		el.onmouseout = function() {
			ArticleController.mouseoverRating();
		}
	},

	'#rating li a' : function(el) {
		el.onmouseover = function() {
			ArticleController.mouseoverRating(el);
		}
		el.onclick = function() {
			try {
				ArticleController.stopPropagation();
				ArticleController.clickRating(el);
			} catch(e) {
				// caught an unhandled exception...
			}
			return false;
		}
	}
});

