/*
	file:			jquery.articles.js
	version:		1.0
	author:			sam jackson
*/

(function(jQuery)
{

	// articlesDefaults contains default options for the plugin
	var articlesDefaults =
	{
		textChars:			-1,					// number of chars to display for the articles text (-1 is all)
		readMoreText:		'read more...',
		linkPrefix:			'',
		invisibleImage:		'',
		fadeSpeed:			100					// animation speed in milliseconds
	}
	
	// articlesIdentifiers contains string id's which are to be attached to certain elements
	var articlesIdentifiers =
	{
		mainContainer:					'container-main',
		imageContainer:					'container-image',
		articlesOuterContainer:			'container-articles-outer',
		articlesInnerContainer:			'container-articles-inner',
		image:							'article-image',
		imageLink:						'article-image-link',
		articlesList:					'articles-list',
		informationContainer:			'container-information',
		infoTitle:						'info-title',
		infoText:						'info-text',
		infoLink:						'info-link'
	};

	// articlesClasses contains string classes which are to be given to certain elements
	var articlesClasses =
	{
		imageContainer:					'container-image',
		image:							'image',
		imageLink:						'image-link',
		articlesList:					'items',
		listItem:						'item',
		informationContainer:			'container-information',
		infoTitle:						'title',
		infoText:						'text',
		infoLink:						'readmore'
	};

	// articlesData contains functions used for getting and setting data unique to the plugins parent element
	var articlesData =
	{
		DATA_NAME:				'articlesdata',
		
		// set is used to assign data to the specified element
		set:					function(element, data)
								{
									if (data != undefined)
									{
										var oldData = articlesData.get(element);
										var extendedData = jQuery.extend({}, oldData, data);
										jQuery.data(element.get(0), articlesData.DATA_NAME, extendedData);
										return data;
									}
									return false;
								},
								
		// get is used to return saved data from the specified element
		get:					function(element)
								{
									return jQuery.data(element.get(0), articlesData.DATA_NAME) || {};
								},
								
		// opts is used to get or set options
		opts:					function(element, options)
								{
									if (options != undefined) return articlesData.set(element, {opts: options}).opts;
									return articlesData.get(element).opts;	
								},
								
		// clss is used to get or set classes
		clss:					function(element, classes)
								{
									if (classes != undefined) return articlesData.set(element, {clss: classes}).clss;
									return articlesData.get(element).clss;	
								},
								
		// articles is used to get or set articles
		articles:				function(element, articles)
								{
									if (articles != undefined) return articlesData.set(element, {articles: articles}).articles;
									return articlesData.get(element).articles;
								}
	};
	
	// articlesFind is used to find and return certain elements
	var articlesFind =
	{
		// elementById is used to lookup an item by its id and then return it
		elementById:			function(allContainer, identifier)
								{
									return allContainer.find("#" + identifier);
								},
								
		// elementsByClass is used to lookup items by class and then return them
		elementsByClass:		function(allContainer, cls)
								{
									return allContainer.find("." + cls);	
								},
								
		mainContainer:			function(allContainer)
								{
									return articlesFind.elementById(allContainer, articlesIdentifiers.mainContainer);
								},
								
		imageContainer:			function(allContainer)
								{
									return articlesFind.elementById(allContainer, articlesIdentifiers.imageContainer);	
								},
								
		articleImage:			function(allContainer)
								{
									return articlesFind.elementById(allContainer, articlesIdentifiers.image);
								},
								
		articleImageLink:		function(allContainer)
								{
									return articlesFind.elementById(allContainer, articlesIdentifiers.imageLink);
								},
								
		articlesList:			function(allContainer)
								{
									return articlesFind.elementById(allContainer, articlesIdentifiers.articlesList);
								},
								
		listItems:				function(allContainer)
								{
									return articlesFind.articlesList(allContainer).children('li');	
								},
								
		informationTitle:		function(allContainer)
								{
									return articlesFind.elementById(allContainer, articlesIdentifiers.infoTitle);
								},
								
		informationText:		function(allContainer)
								{
									return articlesFind.elementById(allContainer, articlesIdentifiers.infoText);
								}
	};
	
	// articlesCreate is used to create and return new elements with some default settings: id, class, css, children
	var articlesCreate =
	{
		mainContainer:				function(classes)
									{
										return $("<div></div>")
											.attr('id', articlesIdentifiers.mainContainer)
											.css({	height:		'100%',
													width:		'100%',
													overflow:	'visible'	});
									},
								
		imageContainer:				function(classes)
									{
										return $("<div></div>")
											.attr('id', articlesIdentifiers.imageContainer)
											.addClass(classes.imageContainer)
											.css({	float:		'left'	});
									},
								
		articlesOuterContainer:		function(classes)
									{
										return $("<div></div>")
											.attr('id', articlesIdentifiers.articlesOuterContainer)
											.css({	float:		'right'	});
									},
									
		articlesInnerContainer:		function(classes)
									{
										return $("<div></div>")
											.attr('id', articlesIdentifiers.articlesInnerContainer)
											.css({	clear:		'both'	});
									},
									
		informationContainer:		function(classes)
									{
										return $("<div></div>")
											.attr('id', articlesIdentifiers.informationContainer)
											.addClass(classes.informationContainer)
											.css( {	clear: 	'both'	} );
									},
									
		articleImage:				function(classes, src, alt)
									{
										if (src == undefined) src = '';
										if (alt == undefined) alt = '';
										
										return $("<img></img>")
											.attr('src', src)
											.attr('alt', alt)
											.attr('id', articlesIdentifiers.image)
											.addClass(classes.image)
											.css('float', 'left');
									},
									
		imageLink:					function(classes, href)
									{
										return $("<a></a>")
											.addClass(classes.imageLink)
											.attr('id', articlesIdentifiers.imageLink)
											.attr('href', href);
									},
									
		articlesList:				function(classes)
									{
										return $("<ul></ul>")
											.attr('id', articlesIdentifiers.articlesList)
											.addClass(classes.articlesList);
									},
									
		listItem:					function(classes, text)
									{
										var item = $("<li></li>")
											.addClass(classes.listItem);
											
										var title = articlesCreate.h3(classes, text).appendTo(item);
											
										return item;
									},
									
		p:							function(classes, text)
									{
										return $("<p></p>")
											.html(text);
									},
									
		h3:							function(classes, text)
									{
										return $("<h3></h3>")
											.html(text);
									},
									
		infoTitle:					function(classes, text)
									{
										return articlesCreate.h3(classes, text)
											.attr('id', articlesIdentifiers.infoTitle)
											.addClass(classes.infoTitle);
									},
									
		infoText:					function(classes, text)
									{
										return articlesCreate.p(classes, text)
											.attr('id', articlesIdentifiers.infoText)
											.addClass(classes.infoText);
									},
										
		infoLink:					function(classes, text, href)
									{
										return $("<a></a>")
											.attr('href', href)
											.html(text)
											.addClass(classes.infoLink)
											.attr('id', articlesIdentifiers.infoLink);
									}
	};

	// articlesBinds contains functions used in binds and all accept the event argument
	var articlesBinds =
	{
		// itemClick is bound to items from the articles list
		itemClick:					function(ev)
									{
										var allContainer = ev.data.allContainer;
										var index = ev.data.articleIndex;
										
										display(allContainer, index);
									}
	};

	// this is the starting point of the plugin
	jQuery.fn.articles = function(command, args)
	{
		
		// loop through each of the passed elements and then return them for chaining
		return this.each(function()
		{
			var container = $(this);
			
			// determine what command was given and act on it appropriately
			
			if (typeof(command) == 'string')
			{
				switch (command)
				{
					case 'initialize':
						initialize(container, args.articles, args.options, args.classes);
						break;
				}
			}
		});
	};
	
	// initialize is used to setup the specified container as an articles display, it will build
	// child elements, position and populate them.
	function initialize(allContainer, articles, options, classes)
	{
		// extend the default options with the specified options and then store them
		var options = articlesData.opts(allContainer, jQuery.extend({}, articlesDefaults, options));
		
		// extend the default classes with the specified classes and then store them
		var classes = articlesData.clss(allContainer, jQuery.extend({}, articlesClasses, classes));
		
		// store the articles
		articlesData.articles(allContainer, articles);
		
		// build the containers for the articles
		buildContainers(allContainer, classes);
		
		// populate the articles list
		populateArticlesList(allContainer, classes, articles);
		
		// display initial article
		display(allContainer, 0, articles, options, classes, 0)
	};
	
	// buildContainers is used to build all of the child containers needed to layout the articles display
	function buildContainers(allContainer, classes)
	{
		// create and append the main container
		var mainContainer = articlesCreate.mainContainer(classes).appendTo(allContainer);
		
		// create and append the image container
		var imageContainer = articlesCreate.imageContainer(classes).appendTo(mainContainer);
		
		// create the link that contains the image
		var imageLink = articlesCreate.imageLink(classes, '#').appendTo(imageContainer);
		
		// create and append the image
		var image = articlesCreate.articleImage(classes).appendTo(imageLink);
		
		// create and append the articles outer container
		var articlesOuterContainer = articlesCreate.articlesOuterContainer(classes).appendTo(mainContainer);
		
		// create and append the articles inner container
		var articlesInnerContainer = articlesCreate.articlesInnerContainer(classes).appendTo(articlesOuterContainer);
		
		// create and append the articles list
		var articlesList = articlesCreate.articlesList(classes).appendTo(articlesInnerContainer);
		
		// create and append the information container
		var informationContainer = articlesCreate.informationContainer(classes).appendTo(mainContainer);
		
		// create and append the information title
		var informationTitle = articlesCreate.infoTitle(classes, 'article title...').appendTo(informationContainer);
		
		// create and append the information text
		var informationText = articlesCreate.infoText(classes, 'article content...').appendTo(informationContainer);
	};
	
	// addReadMoreLink will simply append a new link element to the specified informationText
	function addReadMoreLink(allContainer, classes, href, text, informationText)
	{
		if (informationText == undefined) informationText = articlesFind.informationText(allContainer);
		if (text == undefined) text = articlesData.opts(allContainer).readMoreText;
		if (href == undefined) href = '#';
		
		var informationLink = articlesCreate.infoLink(classes, text, href).appendTo(informationText);
	};
	
	// displayArticle will change the currently displayed article to the specified one
	function displayArticle(allContainer, index, articles, options, classes, speed)
	{
		if (articles == undefined) articles = articlesData.articles(allContainer);
		if (options == undefined) options = articlesData.opts(allContainer);
		if (classes == undefined) classes = articlesData.clss(allContainer);
		if (speed == undefined) speed = options.fadeSpeed;
		
		// get article elements
		var articleImage = articlesFind.articleImage(allContainer);
		var articleTitle = articlesFind.informationTitle(allContainer);
		var articleText = articlesFind.informationText(allContainer);
		var articleImageLink = articlesFind.articleImageLink(allContainer);
		var imageContainer = articlesFind.imageContainer(allContainer);
		
		// fade out current selection
		articleImage.animate({opacity: 0}, speed);
		articleText.animate({opacity: 0}, speed);
		articleTitle.animate({opacity: 0}, speed, function()
			{
				var image_path = articles[index].IMAGE;
				
				imageContainer.removeClass('noimage');
				if (articles[index].IMAGE === '' || articles[index].IMAGE === null)
				{
					imageContainer.addClass('noimage');
					image_path = options.invisibleImage;
				}
				
				// update the article image element
				articleImage.attr('src', image_path);
				
				// upadte the article image link elements href
				articleImageLink.attr('href', options.linkPrefix + articles[index].LINK);
				
				// update the article title element
				articleTitle.html(articles[index].TITLE);
				
				// trim the text as defined in the options
				var trimmedText = articles[index].TEXT;
				if (trimmedText === null) trimmedText = '';
				else if (options.textChars > -1 && trimmedText.length > options.textChars) trimmedText = trimmedText.substring(0, options.textChars) + "...";
					
				// update the article text element
				articleText.html(trimmedText);
				
				// add the readmore link
				addReadMoreLink(allContainer, classes, options.linkPrefix + articles[index].LINK);
				
				// fade in the new selection
				if (articles[index].IMAGE !== '') articleImage.animate({opacity: 1}, speed);
				articleTitle.animate({opacity: 1}, speed);
				articleText.animate({opacity: 1}, speed);
			});
	};
	
	// populateArticlesList will loop through each article in the specified array and add it to a list child
	// element which is then displayed in the articles list
	function populateArticlesList(allContainer, classes, articles, articlesList)
	{
		if (articlesList == undefined) articlesList = articlesFind.articlesList(allContainer);
		if (articles == undefined) articles = articlesData.articles(allContainer);
				
		var articlesCount = articles.length;
		for (var i = 0; i < articlesCount; i++)
		{
			// create and append a new list item to the articles list
			var listItem = articlesCreate.listItem(classes, articles[i].SHORT_TITLE)
				.attr('title', articles[i].FULL_TITLE)
				.appendTo(articlesList);
					
			listItem.bind('click', {allContainer: allContainer, articleIndex: i}, articlesBinds.itemClick);
		}
	};
	
	// updateItemSelection will remove the 'selected' class from all of the list child elements except the
	// one at the specified index
	function updateItemSelection(allContainer, selectedIndex)
	{
		var items = articlesFind.listItems(allContainer);
		
		items.removeClass('selected');
		$(items.get(selectedIndex)).addClass('selected');
	};
	
	// display is used to display the specified article and also update the list child elements to
	// give visual feedback
	function display(allContainer, index, articles, options, classes, speed)
	{
		displayArticle(allContainer, index, articles, options, classes, speed);
		updateItemSelection(allContainer, index);
	};

})(jQuery);