﻿/*
* MoreLessifier - jQuery plugin 0.1
*
* Copyright (c) 2009 Scott L. Mitchell
*
*/

(function($) {
    var _trailingWhitespace = true;

    $.fn.moreLessify = function(options) {

        var opts = $.extend({}, $.fn.moreLessify.defaults, options);

        $(this).each(function() {

            var contentLength = $.trim(collapseWhitespace($(this).text())).length;
            if (contentLength <= opts.maxLength) {
                return;
            }
            else {
                var actualMaxLength = opts.maxLength - opts.moreText.length;
                var truncatedNode = recursivelyTruncate(this, actualMaxLength);
                var originalNode = $(this).hide();

                truncatedNode.insertAfter(originalNode);

                findNodeForMore(truncatedNode).append(' <a class="' + opts.moreClass + '" href="#show more content" rel="nofollow">' + opts.moreText + '</a>');
                findNodeForLess(originalNode).append(' <a class="' + opts.lessClass + '" href="#show less content" rel="nofollow">' + opts.lessText + '</a>');

                truncatedNode.find('a:last').click(function() {
                    truncatedNode.hide(); originalNode.show(500); return false;
                });

                originalNode.find('a:last').click(function() {
                    truncatedNode.show(); originalNode.hide(); return false;
                });
            }
        });
    }

    $.fn.moreLessify.defaults = {
        maxLength: 128,
        moreText: 'more',
        moreClass: 'moreLessLink moreLink',
        lessText: 'less',
        lessClass: 'moreLessLink lessLink'
    };

    function recursivelyTruncate(node, maxLength) {

        return (node.nodeType == 3) ? truncateText(node, maxLength) : truncateNode(node, maxLength);

        /*
        if (node.nodeType == 3)
        {
        return truncateText(node, maxLength);
        }
        else
        {
        truncateNode(node, maxLength);
        }
        */
    }

    function truncateNode(node, maxLength) {
        var node = $(node);
        var newNode = node.clone().empty();
        var truncatedChild;

        node.contents().each(function() {
            var remainingLength = maxLength - newNode.text().length;
            if (remainingLength == 0) {
                return;  // breaks the loop
            }
            else {
                truncatedChild = recursivelyTruncate(this, remainingLength);
                if (truncatedChild) {
                    newNode.append(truncatedChild);
                }
            }
        });

        return newNode;
    }

    function truncateText(node, maxLength) {
        var text = collapseWhitespace(node.data);
        if (_trailingWhitespace) {
            text = text.replace(/^ /, '');
        }
        _trailingWhitespace = !!text.match(/ $/);

        var text = text.slice(0, maxLength);
        text = $('<div/>').text(text).html();
        return text;
    }

    function collapseWhitespace(string) {
        return string.replace(/\s+/g, ' ');
    }

    function findNodeForMore(node) {
        var $node = $(node);
        var lastChild = $node.children(":last");

        if (!lastChild) {
            return node;
        }
        else {
            var display = lastChild.css('display');
            if (!display || display == 'inline') {
                return $node;
            }
            return findNodeForMore(lastChild);
        }
    };

    function findNodeForLess(node) {
        var $node = $(node);
        var lastChild = $node.children(":last");
        if (lastChild && lastChild.is('p')) {
            return lastChild;
        }
        else {
            return node;
        }
    };

})

(jQuery);

