
(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();
            var startText = allText.slice(0, o.slicePoint).replace(/\w+$/,'');
            startTags = startText.match(/<\w[^>]*>/g);
            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="#" class="expand-scroller">',
                    o.expandText,
                    '</a>',
                    '</span>',
                    '<span class="details">',
                    endText,
                    '</span>',
					o.expandPostfix
                    ].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('<a href="#" class="expand-scroller" id="expand-collapse">' + o.userCollapseText + '</a>');

                $this.find('a#expand-collapse').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(/\//,'');
        }
    };

    $.fn.expander.defaults = {
        slicePoint         : 100,
        widow              : 4,
        expandText         : 'read more',
        expandPrefix       : '&hellip; ',
		expandPostfix      : '',
        collapseTimer      : 0,
        expandEffect       : 'fadeIn',
        expandSpeed        : '',
        userCollapse       : true,
        userCollapseText   : '(collapse)',
        userCollapsePrefix : ' ',
        beforeExpand       : function($thisEl) {},
        afterExpand        : function($thisEl) {},
        onCollapse         : function($thisEl, byUser) {}
    };
})(jQuery);




