﻿(function($) {
	$.fn.listnav = function(options) {
		var opts = $.extend({},
		$.fn.listnav.defaults, options);
		var letters = ['А', 'Б', 'В', 'Г', 'Д', 'Е', 'Ж', 'З', 'И', 'К', 'Л', 'М', 'Н', 'О', 'П', 'Р', 'С', 'У', 'Ф', 'Х', 'Ц', 'Ч', 'Ш', 'Щ', 'Э', 'Ю', 'Я'];

		return this.each(function() {
			var $wrapper, list, $list, $letters, $letterCount, id;
			id = this.id;
			$wrapper = $('#' + id + '-nav'); // user must abide by the convention: <ul id="myList"> for list and <div id="myList-nav"> for nav wrapper
			$list = $(this);

			var counts, allCount, isAll, prevLetter;
			counts = [];
			allCount = 0;
			isAll = true;
			prevLetter = '';

			function init() {
				$wrapper.append(createLettersHtml());

				$letters = $('.ln-letters', $wrapper).slice(0, 1); // will always be a single item
				if (opts.showCounts) $letterCount = $('.ln-letterCount', $wrapper).slice(0, 1); // will always be a single item

				$('.z', $letters).addClass(opts.lastClass); // allows for styling a case where last item needs right border set (because items before that only have top, left and bottom so that border between items isn't doubled)

				addLIClasses();
				addNoMatchLI();
				storeLetterCounts();
				bindHandlers();

				if (!opts.includeAll) $list.show(); // show the list in case the recommendation for includeAll=false was taken

				// decide whether to show all or click on a letter
				//
				if (!opts.includeAll) $('.all', $letters).hide();

				if (opts.initLetter != '') $('.' + opts.initLetter, $letters).slice(0, 1).click(); // click the initLetter if there was one
				else {
					if (opts.includeAll) $('.all', $letters).addClass(opts.selectedClass); // showing all: we don't need to click this: the whole list is already loaded
					else { // ALL link is hidden, click the first letter that will display LI's
						for (var i = 0; i < counts.length; i++) {
							if (counts[i] > 0) {
								$('.' + letters[i].toLowerCase(), $letters).slice(0, 1).click();
								break;
							}
						}
					}
				}
			}

			// positions the letter count div above the letter links (so we only have to do it once: after this we just change it's left position via mouseover)
			//
			function setLetterCountTop() {
				$letterCount.css({
					top: $('.a', $letters).slice(0, 1).offset({
						margin: false,
						border: true
					}).top - $letterCount.outerHeight({
						margin: true
					})
				}); // note: don't set top based on '.all': it might not be visible
			}

			// adds a class to each LI that has text content inside of it (ie, inside an <a>, a <div>, nested DOM nodes, etc)
			//
			function addLIClasses() {
				var str;
				$('>li', $list).each(function() {
					str = $(this).text().replace(/\s+/g, '').toLowerCase(); // strip all white space from text (including tabs and linebreaks that might have been in the HTML) // thanks to Liam Byrne, liam@onsight.ie
					if (str != '') $(this).addClass(str.slice(0, 1)); // uses the first letter of the text in the class name
				});
			}

			// populate the counts, allCount
			//
			function storeLetterCounts() {
				var count = 0;
				var letter;
				for (var i = 0; i < letters.length; i++) {
					letter = letters[i].toLowerCase();
					count = $('>li.' + letter, $list).length;
					counts.push(count);
					allCount += count;

					// if the count is 0 and we're supposed to flag the disabled items, add the disabled class
					//
					if (opts.flagDisabled && (count == 0)) $('.' + letter, $letters).addClass(opts.disabledClass);
				}
			}

			function addNoMatchLI() {
				$list.append('<li class="noMatch" style="display:none">' + opts.noMatchText + '</li>');
			}

			function getLetterCount(el) {
				return ($(el).hasClass('all')) ? allCount: counts[$.inArray($(el).attr('class').split(' ')[0].toUpperCase(), letters)]; // doing [0] because we might have class="a disabled" (disabled will always be after the letter)
			}

			function bindHandlers() {

				// sets the top position of the count div in case something above it on the page has resized
				//
				if (opts.showCounts) {
					$wrapper.mouseover(function() {
						setLetterCountTop();
					});
				}

				// mouseover for each letter: shows the count above the letter
				//
				if (opts.showCounts) {
					$('a', $letters).mouseover(function() {
						var left = $(this).position().left;
						var width = ($(this).outerWidth({
							margin: true
						}) - 1) + 'px'; // the -1 is to tweak the width a bit due to a seeming inaccuracy in jquery ui/dimensions outerWidth (same result in FF2 and IE6/7)
						var count = getLetterCount(this);
						$letterCount.css({
							left: left,
							width: width
						}).text(count).show(); // set left position and width of letter count, set count text and show it
					});

					// mouseout for each letter: hide the count
					//
					$('a', $letters).mouseout(function() {
						$letterCount.hide();
					});
				}

				// click handler for letters: shows/hides relevant LI's
				//
				$('a', $letters).click(function() {
					$('a.' + opts.selectedClass, $letters).removeClass(opts.selectedClass);

					var letter = $(this).attr('class').split(' ')[0];

					if (letter == 'all') {
						$('>li', $list).show();
						$('>li.noMatch', $list).hide();
						isAll = true;
					} else {
						if (isAll) {
							$('>li', $list).hide();
							isAll = false;
						} else if (prevLetter != '') $('>li.' + prevLetter, $list).hide();

						var count = getLetterCount(this);
						if (count > 0) {
							$('>li.noMatch', $list).hide(); // in case it's showing
							$('>li.' + letter, $list).show();
						} else $('>li.noMatch', $list).show();

						prevLetter = letter;
					}

					$(this).addClass(opts.selectedClass);
					$(this).blur();
					return false;
				});
			}

			// creates the HTML for the letter links
			//	
			function createLettersHtml() {
				var html = [];
				for (var i = 0; i < letters.length; i++) {
					if (html.length == 0) html.push('<a class="all" href="#">ALL</a>');
					html.push('<a class="' + letters[i].toLowerCase() + '" href="#">' + letters[i] + '</a>');
				}
				return '<div class="ln-letters">' + html.join('') + '</div><div style="clear:both"></div>' + ((opts.showCounts) ? '<div class="ln-letterCount" style="display:none; position:absolute; top:0; left:0; width:20px;">0</div>': ''); // the styling for letterCount is to give us a starting point for the element, which will be repositioned when made visible (ie, should not need to be styled by the user)
			}

			init();
		});
	};

	$.fn.listnav.defaults = {
		initLetter: '',
		includeAll: true,
		flagDisabled: true,
		noMatchText: 'No matching entries',
		lastClass: 'ln-last',
		selectedClass: 'ln-selected',
		disabledClass: 'ln-disabled',
		showCounts: true
	};
})(jQuery);