/*
 * jQuery Expander plugin
 * Version 0.4  (12/09/2008)
 * @requires jQuery v1.1.1+
 *
 * Dual licensed under the MIT and GPL licenses:
 * http://www.opensource.org/licenses/mit-license.php
 * http://www.gnu.org/licenses/gpl.html
 *
 */

(function($) {

	$.fn.expander = function(options) {
	
		var opts = $.extend({}, $.fn.expander.defaults, options);
		var delayedCollapse;
		return this.each(function() {
			var $this = $(this);
			var o = $.meta ? $.extend({}, opts, $this.data()) : opts;
			var cleanedTag, startTags, endTags;	
			var allText = $this.html();
			//alert(allText);
			var startText = allText.slice(0, o.slicePoint).replace(/\w+$/,'');
			startTags = startText.match(/<\w[^>]*>/g);
			//alert(startText);
			if (startTags) {startText = allText.slice(0,o.slicePoint + startTags.join('').length).replace(/\w+$/,'');}
			
			if (startText.lastIndexOf('<') > startText.lastIndexOf('>') ) {
				startText = startText.slice(0,startText.lastIndexOf('<'));
			}
			var endText = allText.slice(startText.length);
			// create necessary expand/collapse elements if they don't already exist
			if (!$('span.details', this).length) {
				// end script if text length isn't long enough.
				if ( endText.replace(/\s+$/,'').split(' ').length < o.widow ) { return; }
				// otherwise, continue...
				if (endText.indexOf('</') > -1) {
					endTags = endText.match(/<(\/)?[^>]*>/g);
					for (var i=0; i < endTags.length; i++) {
		
						if (endTags[i].indexOf('</') > -1) {
							var startTag, startTagExists = false;
							for (var j=0; j < i; j++) {
								startTag = endTags[j].slice(0, endTags[j].indexOf(' ')).replace(/(\w)$/,'$1>');
								if (startTag == rSlash(endTags[i])) {
								  startTagExists = true;
								}
							}              
							if (!startTagExists) {
								startText = startText + endTags[i];
								var matched = false;
								for (var s=startTags.length - 1; s >= 0; s--) {
									if (startTags[s].slice(0, startTags[s].indexOf(' ')).replace(/(\w)$/,'$1>') == rSlash(endTags[i]) 
									&& matched == false) {
										cleanedTag = cleanedTag ? startTags[s] + cleanedTag : startTags[s];
										matched = true;
									}
								};
							}
						}
					}
		
					endText = cleanedTag && cleanedTag + endText || endText;
				}
				$this.html([
					startText,
					'<span class="read-more">',
					o.expandPrefix,
					'<a href="javascript:;">',
					  o.expandText,
					'</a>',
					'</span>',
					'<span class="details">',
					endText,
					'</span>'
					].join(''));
			}
			var $thisDetails = $('span.details', this),
			$readMore = $('span.read-more', this);
			$thisDetails.hide();
			$readMore.find('a').click(function() {
				$readMore.hide();
		
				if (o.expandEffect === 'show' && !o.expandSpeed) {
					o.beforeExpand($this);
					$thisDetails.show();
					o.afterExpand($this);
					delayCollapse(o, $thisDetails);
				} else {
					o.beforeExpand($this);
					$thisDetails[o.expandEffect](o.expandSpeed, function() {
						$thisDetails.css({zoom: ''});
						o.afterExpand($this);
						delayCollapse(o, $thisDetails);
					});
				}
				return false;
			});
			if (o.userCollapse) {
				$this.find('span.details').append('<span class="re-collapse">' + o.userCollapsePrefix + '<a href="javascript:;">' + o.userCollapseText + '</a></span>');
				$this.find('span.re-collapse a').click(function() {
					clearTimeout(delayedCollapse);
					var $detailsCollapsed = $(this).parents('span.details');
					reCollapse($detailsCollapsed);
					o.onCollapse($this, true);
					return false;
				});
			}
		});
		function reCollapse(el) {
			el.hide().prev('span.read-more').show();
		}
		function delayCollapse(option, $collapseEl) {
			if (option.collapseTimer) {
				delayedCollapse = setTimeout(function() {  
					reCollapse($collapseEl);
					option.onCollapse($collapseEl.parent(), false);
				},
					option.collapseTimer
				);
			}
		}
		function rSlash(rString) {
			return rString.replace(/\//,'');
		}
	};
	// plugin defaults
	$.fn.expander.defaults = {
		slicePoint:       100,  // the number of characters at which the contents will be sliced into two parts. 
								// Note: any tag names in the HTML that appear inside the sliced element before 
								// the slicePoint will be counted along with the text characters.
		widow:            4,  // a threshold of sorts for whether to initially hide/collapse part of the element's contents. 
							  // If after slicing the contents in two there are fewer words in the second part than 
							  // the value set by widow, we won't bother hiding/collapsing anything.
		expandText:       'read more', // text displayed in a link instead of the hidden part of the element. 
										  // clicking this will expand/show the hidden/collapsed text
		expandPrefix:     '&hellip; ',
		collapseTimer:    0, // number of milliseconds after text has been expanded at which to collapse the text again
		expandEffect:     'fadeIn',
		expandSpeed:      '',   // speed in milliseconds of the animation effect for expanding the text
		userCollapse:     true, // allow the user to re-collapse the expanded text.
		userCollapseText: '[collapse expanded text]',  // text to use for the link to re-collapse the text
		userCollapsePrefix: ' ',
		beforeExpand: function($thisEl) {},
		afterExpand: function($thisEl) {},
		onCollapse: function($thisEl, byUser) {}
	};
})(jQuery);
